//#define DebugDeserialization

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;
using System.Collections;
using System.Reflection;
using System.Security.Permissions;
using System.ComponentModel;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Data;
using System.Data.Odbc;
using NumericalRecipes;
using FftLib;
using BmgNet;
using StaticUtility;

namespace ObjectLib
{
	public abstract class PathF : CollectionBase, IComparable, IXmlSerializable
	{
		/// <summary>
		/// The ObjectParser application
		/// </summary>
		[NonSerialized]
		public IFormParse Parser;
		/// <summary>
		/// The ParsedObject which owns the current PathF.
		/// </summary>
		[NonSerialized]
		public ParsedObject Owner;
        public string Name = "";
        public Color Color = Color.Transparent;
		#region CollectionBase
		public virtual VertexF this[ int index ]
		{
			get  
			{
				return( (VertexF) List[index] );
			}
			set  
			{
				pbPathChangeCt = 0u;
				List[index] = value;
			}
		}

		/// <summary>
		/// Maintains a standardized order by standardizing when the polygon has
		/// either 3 or 4 vertices.
		/// </summary>
		/// <param name="vf"></param>
		/// <returns></returns>
		public virtual int Add( VertexF vf )
		{
			pbPathChangeCt = 0u;
			return List.Add( vf );
		}

		public virtual int IndexOf( VertexF vf )
		{
			return( List.IndexOf( vf ) );
		}

		/// <summary>
		/// Maintains a standardized order by standardizing when the polygon has
		/// either 3 or 4 vertices.
		/// </summary>
		/// <param name="index"></param>
		/// <param name="vf"></param>
		public virtual void Insert( int index, VertexF vf )  
		{
			pbPathChangeCt = 0u;
			List.Insert( index, vf );
		}

		public virtual void Remove( VertexF vf )  
		{
			pbPathChangeCt = 0u;
			List.Remove( vf );
		}
		public new void RemoveAt(int index)
		{
			pbPathChangeCt = 0u;
			List.RemoveAt( index );
		}
		public virtual bool Contains( VertexF vf )  
		{
			// If value is not of type VertexF, this will return false.
			return( List.Contains( vf ) );
		}

		protected override void OnInsert( int index, object o )
		{
			if ( o.GetType()!=typeof(VertexF) )
				throw new ArgumentException( "value must be of type VertexF.", "value" );
			base.OnInsert(index, o);
		}
		protected override void OnRemove( int index, object o )
		{
			if ( o.GetType()!=typeof(VertexF) )
				throw new ArgumentException( "value must be of type VertexF.", "value" );
			base.OnRemove(index, o);
		}
		protected override void OnSet( int index, object oldValue, object newValue )
		{
			if ( newValue.GetType()!=typeof(VertexF) )
				throw new ArgumentException( "newValue must be of type VertexF.", "newValue" );
			base.OnSet(index, oldValue, newValue);
		}
		protected override void OnValidate( object o )
		{
			if ( o.GetType()!=typeof(VertexF) )
				throw new ArgumentException( "value must be of type VertexF." );
			base.OnValidate(o);
		}
		#endregion
		#region Drawing with ObjectParser
		[NonSerialized]
		private PointF[] pbPath;
		/// <summary>
		/// Gets the picture-box coordinates of a polygon.
		/// </summary>
		public PointF[] PbPath
		{
			get
			{
				//	The contents of this IF statement catches a bug in one of the files.  There
				//	is a polygon that was deleted but still remains linked by the VertexF objects
				//	that it used to own.  This will fix the problem if it ever happens again.
				if( Owner==null )
				{
					this.RemoveVertexF();
					return null;
				}

				bool update = false;
				if(pbPath == null)
				{
					update = true;
					pbPath = new PointF[this.Count];
				}
				else if(pbPath.Length != this.Count)
				{
					update = true;
					pbPath = new PointF[this.Count];
				}
				else if(pbPathChangeCt != Parser.GetImgRectChangeCt())
					update = true;

				if(update)
				{
					Parser.PathToPointTable(this, pbPath);
					pbPathChangeCt = Parser.GetImgRectChangeCt();
				}
				
				return pbPath;
			}
		}
		[NonSerialized]
		protected uint pbPathChangeCt;
		/// <summary>
		/// Gets the rectangle that surrounds the polygon (with extra cushion).
		/// </summary>
		/// <param name="cushion">float  Pushes each vertex of the rectangle out by this extra amount.</param>
		/// <returns>RectangleF  The bounding rectangle with vertices pushed out by the amount of cushion.</returns>
		public RectangleF GetBoundingRectF(float cushion)
		{
			if(this.Count==0) return new RectangleF(0.0f,0.0f,0.0f,0.0f);
			float l,r,t,b;
			l=r=this[0].X;
			t=b=this[0].Y;
			
			for(int i=1; i<this.Count; i++)
			{
				if(this[i].X<l) l=this[i].X;
				if(this[i].X>r) r=this[i].X;
				if(this[i].Y<t) t=this[i].Y;
				if(this[i].Y>b) b=this[i].Y;
			}
			return new RectangleF( l-cushion, t-cushion, r-l+1.0f+2.0f*cushion, b-t+1.0f+2.0f*cushion );
		}
		/// <summary>
		/// Gets the rectangle that surrounds the polygon (with extra cushion).
		/// </summary>
		/// <param name="cushion">float  Pushes each vertex of the rectangle out by this extra amount.</param>
		/// <returns>Rectangle  The bounding rectangle with vertices pushed out by the amount of cushion.  This will be slightly larger
		/// than the rectangle returned by GetBoundingRectF because rounding insures that at least the GetBoundingRectF will be surrounded.</returns>
		public Rectangle GetBoundingRect(float cushion)
		{
			RectangleF rf = GetBoundingRectF(cushion);
			int l = (int)rf.Left;
			int r = (int)(rf.Right+1.0f);
			int t = (int)rf.Top;
			int b = (int)(rf.Bottom+1.0f);
			return new Rectangle(l, t, r-l+2, b-t+2);
		}
		#endregion
		#region IComparable
		[NonSerialized]
		private int _thisHash, _thatHash;
		/// <summary>
		/// Uses the hashcodes to compare polygons for sorting purposes.
		/// </summary>
		/// <param name="obj">PolygonF  The object to be compared.</param>
		/// <returns>-1 if this hashcode is smaller than the comparison's,
		/// 1 if larger, 0 if equal.</returns>
		public int CompareTo(object obj)
		{
            if( this.GetType()==typeof(PolygonF) && obj.GetType()==typeof(ContourF) )
                return -1;
            if( this.GetType()==typeof(ContourF) && obj.GetType()==typeof(PolygonF) )
                return 1;
            PathF pf = (PathF)obj;
            _thisHash = this.GetHashCode();
            _thatHash = pf.GetHashCode();
            if(_thisHash < _thatHash)
                return -1;
            else if(_thisHash > _thatHash)
                return 1;
            return 0;
		}
		#endregion
		#region Ribboning
		/// <summary>
		/// The length of a PathF.  Overriden by PolygonF to be the length of the polygon's perimeter.
		/// </summary>
		public virtual float Circumfrence
		{
			get
			{
				if(this.Count < 2)
					return 0.0f;
				float output = 0.0f;
				for(int i=0; i<this.Count-1; i++)
					output += VertexF.Distance(this[i], this[i+1]);
				return output;
			}
		}
		/// <summary>
		/// Returns a list of edge points that all fall on the path, from beginning to end,
		/// where the distance between the edge points is denoted as dpix.  This is overriden
		/// by PolygonF.
		/// </summary>
		/// <param name="dpix"></param>
		/// <returns></returns>
		public virtual PointF[] EdgePoints(float dpix)
		{
			if(this.Count < 2)
				return null;

            float carry = 0.0f;

			return EdgePoints(dpix,this[0],this[this.Count-1],true,ref carry);
		}
		/// <summary>
		/// Gives the length of a portion of the path from vfBegin to vfEnd.  Overriden by PolygonF.
		/// </summary>
		/// <param name="vfBegin"></param>
		/// <param name="vfEnd"></param>
		/// <param name="ascending"></param>
		/// <returns></returns>
		public virtual double EdgeLength(VertexF vfBegin, VertexF vfEnd, bool ascending)
		{
            if(vfBegin==vfEnd && !(vfBegin==this[0] && this[0]==this[this.Count-1]))
				return 0.0;	//	overriden by PolygonF to be this.Circumference
            int I=0;
            int J=this.Count-1;

			double output = 0.0;
            if(vfBegin!=vfEnd)
            {
			    I = IndexOf(vfBegin);
			    J = IndexOf(vfEnd);
            }
			if(ascending && I>J || !ascending && J>I)
				return 0.0;
			int i=I, j;
			double dx, dy;
			VertexF vfi, vfj;

			if(I==-1 || J==-1)
				return 0.0;

			while(true)
			{
				//	j denotes the next vertex
				if(ascending)
				{
					j=i+1;
					if(j==Count)
						return 0.0;
				}
				else
				{
					j=i-1;
					if(j==-1)
						return 0.0;
				}

				vfi = this[i];
				vfj = this[j];
				dx = (double)(vfj.X-vfi.X);
				dy = (double)(vfj.Y-vfi.Y);
				output += Math.Sqrt(dx*dx+dy*dy);

				if(J==j)	//	This is the last trip through
					return output;

				//	Increment (or decrement)
				if(ascending)
				{
					i++;
				}
				else
				{
					i--;
				}
			}
		}
		/// <summary>
		/// This is overriden by PolygonF.
		/// Returns a list of edge points that fall within this Edge, from beginning to end,
		/// where the distance between the edge points is denoted as dpix.
		/// </summary>
		/// <param name="dpix">The distance between edge points in pixels.</param>
		/// <param name="vfBegin">The edge starts at this vertex.</param>
		/// <param name="vfEnd">The edge ends at this vertex.</param>
		/// <param name="ascending">True if the order of vertices is ascending, false otherwise.</param>
		/// <param name="carryOver">On input, the distance already crossed leading up to the first edge point.
		/// On output, the distance trailing the last edge point.</param>
		/// <returns>An array of PointF objects spaced apart by the distance of 'dpix'.</returns>
        public virtual PointF[] EdgePoints(float dpix,VertexF vfBegin,VertexF vfEnd,bool ascending,ref float carryOver)
        {
            //  Cannot continue unless there are at least 2 vertices in this path.
            if(Count < 2)
                return null;

            int i=0,lastInd=0;

            //	i denotes the current vertex
            if(vfBegin==vfEnd && !(vfBegin==this[0] && this[0]==this[this.Count-1]) )	//	empty
            {
                return new PointF[0];
            }
            else				//	not empty
            {
                if( vfBegin==vfEnd )
                {
                    i=0;
                    lastInd = this.Count-1;
                }
                else
                {
                    i=IndexOf(vfBegin);
                    lastInd = IndexOf(vfEnd);
                }
                if((ascending && i>lastInd) || (!ascending && i<lastInd))
                    return null;
            }

            if(carryOver<0.0f || carryOver >= 1.0f)
                throw new ArgumentException("Value must be non-negative and less than 1","carryOver");

            int len = (int)(EdgeLength(vfBegin,vfEnd,ascending)/dpix+(double)carryOver);
            if(len<1)
            {
                carryOver+=(float)EdgeLength(vfBegin,vfEnd,ascending)/dpix;
                return new PointF[0];
            }

            //	Allocate the output
            PointF[] output = new PointF[len];

            //	Denotes the amount of weight to be carried over.
            float wPix,dx,dy,wSteps;
            int ct=0,j;

            //	i denotes the current vertx
            do
            {
                //	j denotes the next vertex
                if(ascending)
                    j=i+1;
                else
                    j=i-1;
                if(j==this.Count)
                    j=0;
                if(j<0)
                    j=this.Count-1;
                //	denote the slope for this interval.
                dx = this[j].X-this[i].X;
                dy = this[j].Y-this[i].Y;
                //cumdist += (float)Math.Sqrt(dx*dx+dy*dy);
                //	count the number of pixels for this interval.
                wPix = (float)Math.Sqrt((double)(dx*dx+dy*dy));
                wSteps = wPix/dpix;
                if(wSteps + carryOver < 1.0f)	//	if there isn't enough to extract an edge point, then move to the next vertex.
                {
                    carryOver+=wSteps;
                }
                else	//	otherwise, within this vertex, get all the edge points.
                {
                    //	Compute the unit vector.
                    dx/=wPix;
                    dy/=wPix;
                    //  Now, carryOver has been passed, its complement awaits us.
                    carryOver = 1.0f-carryOver;
                    if(ct<output.Length) // This safety is here due to roundoff error when EdgeLength / dpix is near an integer.
                    {
                        do
                            output[ct++] = new PointF(this[i].X+dx*carryOver*dpix,this[i].Y+dy*carryOver*dpix);
                        while(++carryOver <= wSteps && ct<output.Length); // Also includes safety
                    }
                    carryOver=carryOver%1.0f;	//	carry over the remainder to the next iteration
                }
                i=j;
            } while(i!=lastInd);

            return output;
        }
		#endregion
		public JunctionF[] JunctionFs
		{
			get
			{
				JunctionFcache = JunctionF.ConstructJunctions(this, true);
				return JunctionFcache;
			}
		}
		[NonSerialized]
		public JunctionF[] JunctionFcache;
		/// <summary>
		/// Determines whether 'this' runs in the same direction as 'path' moving from iThis to jThis.  Returns +1
		/// if 'path' runs from iPath to jPath, returns -1 if 'path' runs from jPath to iPath and returns 0 if indeterminate.
		/// It is acceptable for either path to run for a time while the other path does not run (i.e. that path leaves
		/// the common edge for a while, then eventually comes back).  This function calls EvidenceForward and EvidenceBackward
		/// to compute a result.
		/// </summary>
		/// <param name="path">Another PathF object for comparison.</param>
		/// <param name="iThis">Index of a VertexF in 'this' which matches a VertexF in 'path' at index iPath.</param>
		/// <param name="jThis">Index of a VertexF in 'this' which matches a VertexF in 'path' at index jPath.</param>
		/// <param name="iPath">Index of a VertexF in 'path' which matches a VertexF in 'this' at index iThis.</param>
		/// <param name="jPath">Index of a VertexF in 'path' which matches a VertexF in 'this' at index jThis.</param>
		/// <returns>+1 if evidence suggests that 'this' and 'path' are ordered in the same direction, -1 if evidence
		/// suggests that 'this' and 'path' are ordered in opposite directions, and 0 if evidence is indeterminate.</returns>
		public int Direction(PathF path, int iThis, int jThis, int iPath, int jPath)
		{
			int x = EvidenceForward(path,iThis,jThis,iPath,jPath) - EvidenceBackward(path,iThis,jThis,iPath,jPath);
			if(x>0)
				return 1;
			if(x<0)
				return -1;
			return 0;
		}
		/// <summary>
		/// Counts evidence that 'this' moving from iThis to jThis (by increment) runs along with 'path' moving from
		/// iPath to jPath (by increment).
		/// It is acceptable for either path to run for a time while the other path does not run (i.e. that path leaves
		/// the common edge for a while, then eventually comes back).
		/// </summary>
		/// <param name="path">Another PathF object for comparison.</param>
		/// <param name="iThis">Index of a VertexF in 'this' which matches a VertexF in 'path' at index iPath.</param>
		/// <param name="jThis">Index of a VertexF in 'this' which matches a VertexF in 'path' at index jPath.</param>
		/// <param name="iPath">Index of a VertexF in 'path' which matches a VertexF in 'this' at index iThis.</param>
		/// <param name="jPath">Index of a VertexF in 'path' which matches a VertexF in 'this' at index jThis.</param>
		/// <returns>The number of VertexF coincidences along either path.</returns>
		public int EvidenceForward(PathF path, int iThis, int jThis, int iPath, int jPath)
		{
			int output = -1;

			//	Counters
			int it=iThis, ip=iPath;

			//	First, try increasing both it and ip simultaneously
			do
			{
				output++;
				if(++it >= this.Count)
					it=0;
				if(++ip >= path.Count)
					ip=0;
			} while(it!=jThis && ip!=jPath && this[it]==path[ip]);
			if(it==jThis || ip==jPath)
				return output;

			//	Both i and j are forced to progress forward by at least 1.  Here, remember 'it' to be restored later (maybe).
			iThis=it;

			//	Try skipping forward along 'this'
			do
			{
				if(++it >= this.Count)
					it=0;
			} while(it!=jThis && this[it]!=path[ip]);
			if(it!=jThis)
				return output + EvidenceForward(path, it, jThis, ip, jPath);

			//	Try skipping forward along 'path'
			it=iThis;
			do
			{
				if(++ip >= path.Count)
					ip=0;
			} while(ip!=jPath && this[it]!=path[ip]);
			if(ip!=jPath)
				return output + EvidenceForward(path, it, jThis, ip, jPath);

			//	The current evidence is all that exists
			return output;
		}
		/// <summary>
		/// Counts evidence that 'this' moving from iThis to jThis (by increment) runs along with 'path' moving from
		/// jPath to iPath (by decrement).
		/// It is acceptable for either path to run for a time while the other path does not run (i.e. that path leaves
		/// the common edge for a while, then eventually comes back).
		/// </summary>
		/// <param name="path">Another PathF object for comparison.</param>
		/// <param name="iThis">Index of a VertexF in 'this' which matches a VertexF in 'path' at index jPath.</param>
		/// <param name="jThis">Index of a VertexF in 'this' which matches a VertexF in 'path' at index iPath.</param>
		/// <param name="iPath">Index of a VertexF in 'path' which matches a VertexF in 'this' at index jThis.</param>
		/// <param name="jPath">Index of a VertexF in 'path' which matches a VertexF in 'this' at index iThis.</param>
		/// <returns>The number of VertexF coincidences along either path.</returns>
		public int EvidenceBackward(PathF path, int iThis, int jThis, int iPath, int jPath)
		{
			int output = -1;

			//	Counters
			int it=iThis, jp=jPath;

			//	First, try increasing both it and ip simultaneously
			do
			{
				output++;
				if(++it >= this.Count)
					it=0;
				if(--jp < 0)
					jp=path.Count-1;
			} while(it!=jThis && jp!=iPath && this[it]==path[jp]);
			if(it==jThis || jp==iPath)
				return output;

			//	i is forced to progress forward by at least 1, and j is forced backward by at least 1.
			//	Here, remember 'it' to be restored later (maybe).
			iThis=it;

			//	Try skipping forward along 'this'
			do
			{
				if(++it >= this.Count)
					it=0;
			} while(it!=jThis && this[it]!=path[jp]);
			if(it!=jThis)
				return output + EvidenceBackward(path, it, jThis, iPath, jp);

			//	Try skipping forward along 'path'
			it=iThis;
			do
			{
				if(--jp < 0)
					jp=path.Count-1;
			} while(jp!=iPath && this[it]!=path[jp]);
			if(jp!=iPath)
				return output + EvidenceBackward(path, it, jThis, iPath, jp);

			//	The current evidence is all that exists
			return output;
		}
		/// <summary>
		/// Returns true if empty.
		/// </summary>
		public virtual bool Empty{	get{	return this.Count <= 0;	}	}
		/// <summary>
		/// True if this PathF object is equal to a null value.
		/// </summary>
		public bool IsNull
		{
			get
			{
				return this==PolygonF.NullPoly || this==ContourF.NullValue;
			}
		}
		/// <summary>
		/// Removes this path from the list of owners for each VertexF, however, each VertexF is left in this path's list of member vertices
        /// in order to accomodate "undo" functionality.  For PolygonF objects, this method is also called for any Contour objects in the
        /// member list 'Contours'.
		/// </summary>
		public virtual void RemoveVertexF()
		{
			throw new ApplicationException("This method must be overridden.");
			//for(int i=0; i<this.Count; i++)
			//	this[i].RemoveOwner(this);
		}
		/// <summary>
		/// Adds this path back into the list of owners for each of the VertexF objects in this path's list of member vertices.
		/// This is the inverse of RemoveVertexF().  If this is a polygon object, all child contour objects will have this method called.
		/// </summary>
		public virtual void ReclaimVertices()
		{
			throw new ApplicationException("This method must be overridden.");
			/*
			int i;
			for(i=0; i<this.Count; i++)
            {
				if(!this[i].Polygons.Contains(this))
					this[i].Polygons.Add(this);
            }
			*/
		}
		/// <summary>
		/// If pf has a vertex that is owned by this polygon, return true.  Otherwise, false.
		/// </summary>
		/// <param name="pf"></param>
		/// <returns></returns>
		public bool SharesVertexWith(PathF pf)
		{
			if(pf.Count < this.Count)
			{
                if (pf.GetType()==typeof(PolygonF))
                {
				    for(int i=0; i<pf.Count; i++)
					    if(pf[i].Polygons.Contains(this))
						    return true;
                }
                else
                {
                    for(int i=0;i<pf.Count;i++)
                        if(pf[i].Contours.Contains(this))
                            return true;
                }
			}
			else
			{
                if (pf.GetType()==typeof(PolygonF))
                {
				    for(int i=0; i<this.Count; i++)
					    if(this[i].Polygons.Contains(pf))
						    return true;
                }
                else
                {
                    for(int i=0;i<this.Count;i++)
                        if(this[i].Contours.Contains(pf))
                            return true;
                }
			}
			return false;
		}
		/// <summary>
		/// Gets any vertex in this PolygonF which is not part of the argument pf.
		/// </summary>
		/// <param name="pf"></param>
		/// <returns></returns>
		public VertexF GetAnyVertexNotPartOf(PathF pf)
		{
			if(this.Count == 0)
				return null;
            if(pf.GetType()==typeof(PolygonF))
            {
			    for(int i=0; i<this.Count; i++)		//	This is real PolygonF
			    {
				    if(this[i].Polygons==null)
					    return this[i];
				    if(!this[i].Polygons.Contains(pf))
					    return this[i];
			    }
            }
            else
            {
                for(int i=0;i<this.Count;i++)		//	This is real PolygonF
                {
                    if(this[i].Contours==null)
                        return this[i];
                    if(!this[i].Contours.Contains(pf))
                        return this[i];
                }
            }
			return null;	//	This line should never be reached.
		}
		/// <summary>
		/// Used in static Intersects(pf1, ii, pf2, ii) function.  Contains 4 elements of type DoubleSort
		/// that are initialized in the static constructor.
		/// </summary>
		[NonSerialized]
		public static ArrayList _angles;
		/// <summary>
		/// Finds all X-junctions for the given paths.  If paths are equal, finds self-intersecting
		/// X-junctions.
		/// </summary>
		/// <param name="ind1">out  This represents the indices of intersection in PathF a for each X-junction.
		/// Non-integer indices represent an interpolated location between two actual integer indices.
		/// For example, an output of 1.4 would represent an index between indices 1 and 2 and nearest to 1.
		/// Each result is always non-negative.  The result is null if no X-junctions exist.</param>
		/// <param name="ind2">Denotes the index of intersection in PathF a.
		/// Non-integer indices represent an interpolated location between two actual integer indices.
		/// For example, an output of 1.4 would represent an index between indices 1 and 2 and nearest to 1.
		/// Each result is always non-negative.  The result is null if no X-junctions exist.</param>
		/// <returns>Returns the point of intersection for each X-junction that exists.  If there are no X-junctions, 
		/// null is returned.</returns>
		public static PointF[] IntersectsX(PathF pf1, PathF pf2, out float[] ind1, out float[] ind2)
		{
			ind1=null;
			ind2 = null;
			
			ArrayList output = new ArrayList();
			ArrayList i1 = new ArrayList();
			ArrayList i2 = new ArrayList();

			PointF res;
			float ires, jres;

			int i,j,iNext,jNext;

			//	For each leg of pf1
			for(i=0; i<pf1.Count; i++)
			{
				//	Skip wrap-around for ContourF objects
				if( i==pf1.Count-1 && pf1.GetType()==typeof(ContourF) )
					break;
				//	Compute the second VertexF of the leg
				iNext = i+1;
				if( iNext==pf1.Count )
					iNext = 0;

				//	For each leg of pf2
				for(j=0; j<pf2.Count; j++)
				{
					//	Skip wrap-around for ContourF objects
					if( j==pf2.Count-1 && pf2.GetType()==typeof(ContourF) )
						break;
					//	Compute the second VertexF of the leg
					jNext = j+1;
					if( jNext==pf2.Count )
						jNext = 0;
					//	Skip overlapping legs when pf1==pf2
					if( pf1!=pf2 || (i!=j && i!=jNext && j!=iNext && iNext!=jNext) )
					{
						//	Check for an intersection
						res = IntersectsX(pf1,i,pf2,j, out ires, out jres);
						//	If exists, store
						if( !res.IsEmpty )
						{
							output.Add(res);
							i1.Add(ires+(float)i);
							i2.Add(jres+(float)j);
						}
					}
				}
			}
			//	Return answer when at least 1 X-junction exists
			if(output.Count>0)
			{
				ind1 = (float[])i1.ToArray(typeof(float));
				ind2 = (float[])i2.ToArray(typeof(float));
				return (PointF[])output.ToArray(typeof(PointF));
			}
			//	Return null when there are no X-junctions.
			return null;
		}
		/// <summary>
		/// This checks to see if two legs from different paths intersect each other as an X-junction.
		/// </summary>
		/// <param name="pf1"></param>
		/// <param name="ii">The index of the earlier vertex on pf1.</param>
		/// <param name="pf2"></param>
		/// <param name="jj">The index of the earlier vertex on pf2.</param>
		/// <param name="iterp">If there is an X-junction, this returns the interpolation coefficient
		/// (within the range [0.0f, 1.0f]) for the leg on pf1.  A coefficient less than 0.5f implies
		/// that the intersection is closest to index ii, but a larger coefficient implies that the 
		/// point of intersection is nearer to the next index.</param>
		/// <param name="jterrp">If there is an X-junction, this returns the interpolation coefficient
		/// (within the range [0.0f, 1.0f]) for the leg on pf2.  A coefficient less than 0.5f implies
		/// that the intersection is closest to index jj, but a larger coefficient implies that the 
		/// point of intersection is nearer to the next index.</param>
		/// <returns>If there is no X-junction, returns PointF.Empty; otherwise, returns the location of intersection
		/// for the X-junction.</returns>
		public static PointF IntersectsX(PathF pf1, int ii,  PathF pf2, int jj, out float iterp, out float jterp)
		{
			iterp = -1.0f;
			jterp = -1.0f;

			int iiNext = VertexF.NextInd(pf1.Count, ii);
			int jjNext = VertexF.NextInd(pf2.Count, jj);

			//	If there are any vertices in common, then this is a T-junction (not X-junction), so return false.
			if( pf1[ii]==pf2[jj] || pf1[iiNext]==pf2[jj] || pf1[ii]==pf2[jjNext] || pf1[iiNext]==pf2[jjNext] )
				return PointF.Empty;

			float m1, m2;

			m1 = (pf1[iiNext].Y-pf1[ii].Y) / (pf1[iiNext].X-pf1[ii].X);
			m2 = (pf2[jjNext].Y-pf2[jj].Y) / (pf2[jjNext].X-pf2[jj].X);

			float cept1;
			float cept2;

			if(Single.IsInfinity(m1) || Single.IsNegativeInfinity(m1))
			{
				if(Single.IsInfinity(m2) || Single.IsNegativeInfinity(m2))
					return PointF.Empty;	//	lines are parallel vertical.

				cept2 = pf2[jj].Y - m2 * pf2[jj].X;
				float x1 = pf1[ii].X;
				float y2 = m2*x1 + cept2;

				//	Get the x-value of the vertical line m1.
				float x2min = Math.Min(pf2[jj].X, pf2[jjNext].X);
				float x2max = Math.Max(pf2[jj].X, pf2[jjNext].X);
				float ymin = Math.Min(pf1[ii].Y, pf1[iiNext].Y);
				float ymax = Math.Max(pf1[ii].Y, pf1[iiNext].Y);
				
				//	If within acceptable x and y values, return true.
				if(  y2>ymin && y2<ymax  && x1>x2min && x1<x2max)
				{
					//	Get iterp (m1 is a range, not a slope anymore)
					m1 = (pf1[iiNext].Y-pf1[ii].Y);
					if(m1==0.0f)
						iterp = 0.5f;
					else if(m1<0.0f)
					{
						m1 = -m1;
						iterp = (y2-pf1[iiNext].Y)/m1;
					}
					else
					{
						iterp = (y2-pf1[ii].Y)/m1;
					}
					//	Get jterp (m2 is a range, not a slope anymore)
					m2 = (pf2[jjNext].X-pf2[jj].X);
					if(m2==0.0f)
						jterp = 0.5f;
					else if(m2<0.0f)
					{
						m2 = -m2;
						jterp = (x1-pf2[jjNext].X)/m2;
					}
					else
					{
						jterp = (x1-pf2[jj].X)/m2;
					}

					//	Return the point of intersection
					return new PointF(x1,y2);
				}

				return PointF.Empty;
			}
			else if(Single.IsInfinity(m2) || Single.IsNegativeInfinity(m2))
			{
				cept1 = pf1[ii].Y - m1 * pf1[ii].X;
				float x2 = pf2[jj].X;
				float y1 = m1*x2 + cept1;

				//	Get the x-value of the vertical line m1.
				float xmin = Math.Min(pf1[ii].X, pf1[iiNext].X);
				float xmax = Math.Max(pf1[ii].X, pf1[iiNext].X);
				float y2min = Math.Min(pf2[jj].Y, pf2[jjNext].Y);
				float y2max = Math.Max(pf2[jj].Y, pf2[jjNext].Y);
				
				//	If within acceptable x and y values, return true.
				if(  y1>y2min && y1<y2max && x2>xmin && x2<xmax )
				{
					//	Get iterp (m1 is a range, not a slope anymore)
					m1 = (pf1[iiNext].X-pf1[ii].X);
					if(m1==0.0f)
						iterp = 0.5f;
					else if(m1<0.0f)
					{
						m1 = -m1;
						iterp = (x2-pf1[iiNext].X)/m1;
					}
					else
					{
						iterp = (x2-pf1[ii].X)/m1;
					}
					//	Get jterp (m2 is a range, not a slope anymore)
					m2 = (pf2[jjNext].Y-pf2[jj].Y);
					if(m2==0.0f)
						jterp = 0.5f;
					else if(m2<0.0f)
					{
						m2 = -m2;
						jterp = (y1-pf2[jjNext].Y)/m2;
					}
					else
					{
						jterp = (y1-pf2[jj].Y)/m2;
					}

					return new PointF(x2,y1);
				}

				return PointF.Empty;
			}
			else
			{
				cept1 = pf1[ii].Y - m1 * pf1[ii].X;
				cept2 = pf2[jj].Y - m2 * pf2[jj].X;

				//	Gives point of intersection for infinitely stretching lines.
				float x = (cept2-cept1)/(m1-m2);
				float y = m1*x + cept1;

				//	Now, to see if the point actually falls on these segments, see if (x,y) falls in the COMMON bounding rect.
			
				float left = Math.Max(Math.Min(pf1[ii].X, pf1[iiNext].X), Math.Min(pf2[jj].X, pf2[jjNext].X));
				float top = Math.Max(Math.Min(pf1[ii].Y, pf1[iiNext].Y), Math.Min(pf2[jj].Y, pf2[jjNext].Y));
				float right = Math.Min(Math.Max(pf1[ii].X, pf1[iiNext].X), Math.Max(pf2[jj].X, pf2[jjNext].X));
				float bottom = Math.Min(Math.Max(pf1[ii].Y, pf1[iiNext].Y), Math.Max(pf2[jj].Y, pf2[jjNext].Y));

				if(x>=left && x<=right && y>=top && y<=bottom)
				{
					//	Get iterp (m1 is a range, not a slope anymore)
					m1 = (pf1[iiNext].X-pf1[ii].X);
					if(m1==0.0f)
						iterp = 0.5f;
					else if(m1<0.0f)
					{
						m1 = -m1;
						iterp = (x-pf1[iiNext].X)/m1;
					}
					else
					{
						iterp = (x-pf1[ii].X)/m1;
					}
					//	Get jterp (m2 is a range, not a slope anymore)
					m2 = (pf2[jjNext].Y-pf2[jj].Y);
					if(m2==0.0f)
						jterp = 0.5f;
					else if(m2<0.0f)
					{
						m2 = -m2;
						jterp = (y-pf2[jjNext].Y)/m2;
					}
					else
					{
						jterp = (y-pf2[jj].Y)/m2;
					}

					return new PointF(x,y);
				}
			}
			return PointF.Empty;
		}
		/// <summary>
		/// See if the two legs of different paths intersect each other.
		/// This method does not consider pf1[.].Owners and pf2[.].Owners.
		/// </summary>
		/// <param name="pf1"></param>
		/// <param name="ii">Index denoting a leg on pf1 by denoting the earlier vertex on that leg.</param>
		/// <param name="pf2"></param>
		/// <param name="jj">Index denoting a leg on pf2 by denoting the earlier vertex on that leg.</param>
		/// <returns></returns>
		public static bool Intersects(PathF pf1, int ii, PathF pf2, int jj)
		{
			//	If the intersection is a shared leg return false
			//	If there is a shared vertex 
			//		if it isn't a crossing junction (1212 or 2121)
			//			return false
			//		else
			//			return true
			//	Else do the Intersection math for all other cases
			int iiNext = VertexF.NextInd(pf1.Count, ii);
			int jjNext = VertexF.NextInd(pf2.Count, jj);
			
			//	If there is a shared vertex 
			if( (pf1[ii]==pf2[jj]) || (pf1[ii]==pf2[jjNext]) || (pf1[iiNext]==pf2[jj]) || (pf1[iiNext]==pf2[jjNext]) )
			{
				if(pf1.Count > 2 && pf2.Count > 2)
				{
					int iShared = ii;
					int jShared = jj;
					if(pf1[iiNext]==pf2[jjNext])
					{
						iShared = iiNext;
						jShared = jjNext;
					}
					else if(pf1[iiNext]==pf2[jj])
						iShared = iiNext;
					else if(pf1[ii]==pf2[jjNext])
						jShared = jjNext;

					int iLast = VertexF.LastInd(pf1.Count, iShared);
					int iNext = VertexF.NextInd(pf1.Count, iShared);

					int jLast = VertexF.LastInd(pf2.Count, jShared);
					int jNext = VertexF.NextInd(pf2.Count, jShared);

					((DoubleSort)_angles[0]).Val = VertexF.Angle(pf1[iShared],pf1[iLast]) % Util.TwoPi;
					((DoubleSort)_angles[1]).Val = VertexF.Angle(pf1[iShared],pf1[iNext]) % Util.TwoPi;
					((DoubleSort)_angles[2]).Val = VertexF.Angle(pf2[jShared],pf2[jLast]) % Util.TwoPi;
					((DoubleSort)_angles[3]).Val = VertexF.Angle(pf2[jShared],pf2[jNext]) % Util.TwoPi;
					((DoubleSort)_angles[0]).Ind = 0;
					((DoubleSort)_angles[1]).Ind = 1;
					((DoubleSort)_angles[2]).Ind = 2;
					((DoubleSort)_angles[3]).Ind = 3;
					_angles.Sort();
					if( ((DoubleSort)_angles[0]).Val == ((DoubleSort)_angles[1]).Val 
						||  ((DoubleSort)_angles[1]).Val == ((DoubleSort)_angles[2]).Val 
						||  ((DoubleSort)_angles[2]).Val == ((DoubleSort)_angles[3]).Val)	//If the intersection is a shared leg
						return false;

					for(int i=0; i<_angles.Count-1; i++)
						if( (((DoubleSort)_angles[i]).Ind<2) == (((DoubleSort)_angles[i+1]).Ind<2) )	//	Not a crossing junction.
							return false;

					//	It is a crossing junction.
					return true;
				}
				else
					return false;
			}
			
			float m1, m2;

			m1 = (pf1[iiNext].Y-pf1[ii].Y) / (pf1[iiNext].X-pf1[ii].X);
			m2 = (pf2[jjNext].Y-pf2[jj].Y) / (pf2[jjNext].X-pf2[jj].X);

			float cept1;
			float cept2;

			if(Single.IsInfinity(m1) || Single.IsNegativeInfinity(m1))
			{
				if(Single.IsInfinity(m2) || Single.IsNegativeInfinity(m2))
					return false;	//	lines are parallel vertical.

				cept2 = pf2[jj].Y - m2 * pf2[jj].X;
				float x1 = pf1[ii].X;
				float y2 = m2*x1 + cept2;

				//	Get the x-value of the vertical line m1.
				float x2min = Math.Min(pf2[jj].X, pf2[jjNext].X);
				float x2max = Math.Max(pf2[jj].X, pf2[jjNext].X);
				float ymin = Math.Min(pf1[ii].Y, pf1[iiNext].Y);
				float ymax = Math.Max(pf1[ii].Y, pf1[iiNext].Y);
				
				//	If within acceptable x and y values, return true.
				if(  y2>ymin && y2<ymax  && x1>x2min && x1<x2max)
					return true;

				return false;
			}
			else if(Single.IsInfinity(m2) || Single.IsNegativeInfinity(m2))
			{
				cept1 = pf1[ii].Y - m1 * pf1[ii].X;
				float x2 = pf2[jj].X;
				float y1 = m1*x2 + cept1;

				//	Get the x-value of the vertical line m1.
				float xmin = Math.Min(pf1[ii].X, pf1[iiNext].X);
				float xmax = Math.Max(pf1[ii].X, pf1[iiNext].X);
				float y2min = Math.Min(pf2[jj].Y, pf2[jjNext].Y);
				float y2max = Math.Max(pf2[jj].Y, pf2[jjNext].Y);
				
				//	If within acceptable x and y values, return true.
				if(  y1>y2min && y1<y2max && x2>xmin && x2<xmax )
					return true;

				return false;
			}
			else
			{
				cept1 = pf1[ii].Y - m1 * pf1[ii].X;
				cept2 = pf2[jj].Y - m2 * pf2[jj].X;

				//	Gives point of intersection for infinitely stretching lines.
				float x = (cept2-cept1)/(m1-m2);
				float y = m1*x + cept1;

				//	Now, to see if the point actually falls on these segments, see if (x,y) falls in the COMMON bounding rect.
			
				float left = Math.Max(Math.Min(pf1[ii].X, pf1[iiNext].X), Math.Min(pf2[jj].X, pf2[jjNext].X));
				float top = Math.Max(Math.Min(pf1[ii].Y, pf1[iiNext].Y), Math.Min(pf2[jj].Y, pf2[jjNext].Y));
				float right = Math.Min(Math.Max(pf1[ii].X, pf1[iiNext].X), Math.Max(pf2[jj].X, pf2[jjNext].X));
				float bottom = Math.Min(Math.Max(pf1[ii].Y, pf1[iiNext].Y), Math.Max(pf2[jj].Y, pf2[jjNext].Y));

				if(x>=left && x<=right && y>=top && y<=bottom)
					return true;
			}
			return false;
		}
		/// <summary>
		/// Checks to see if two paths intersect.
		/// </summary>
		/// <param name="pf1"></param>
		/// <param name="pf2"></param>
		/// <returns></returns>
		public static bool Intersects(PathF pf1, PathF pf2)
		{
			if(pf1==null || pf2==null)
				return false;
			for(int i=0; i<pf1.Count-1 || (i<pf1.Count && pf1.GetType()==typeof(PolygonF)); i++)
			{
				for(int j=0; j<pf2.Count-1 || (j<pf2.Count && pf2.GetType()==typeof(PolygonF)); j++)
				{					
					if(Intersects(pf1,i,pf2,j))
						return true;
				}
			}
			return false;
		}
		/// <summary>
		///	For some reason, I was not able to program this as a non-static method.  The environment responded
		///	strangely to the "this" operator and indicated errors in the code even though the code compiled perfectly.
		///	Returns true if the side beginning with index "a" intersects the side beginning with index "b"
		/// (unless "a" and "b" are adjacent legs, in which case false is returned).  This is used to check if
		/// a polygon crosses over on itself.  Driven by IntersectSelf( [empty] )
		/// </summary>
		/// <param name="pf"></param>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public static bool IntersectsSelf(PathF pf, int a, int b)
		{
			int aNext = VertexF.NextInd(pf.Count, a);
			int bNext = VertexF.NextInd(pf.Count, b);
			if(aNext==b || bNext==a)	//	legs are adjacent
				return false;
			if(a==b)
				return false;

			float ma, mb;

			ma = (pf[aNext].Y-pf[a].Y) / (pf[aNext].X-pf[a].X);
			mb = (pf[bNext].Y-pf[b].Y) / (pf[bNext].X-pf[b].X);

			float acept;
			float bcept;

			acept = pf[a].Y - ma * pf[a].X;
			bcept = pf[b].Y - mb * pf[b].X;

			//	Gives point of intersection for infinitely stretching lines.
			float x = (bcept-acept)/(ma-mb);
			float y = ma*x + acept;

			//	Now, to see if the point actually falls on these segments, see if (x,y) falls in the COMMON bounding rect.
			
			float left = Math.Max(Math.Min(pf[a].X, pf[aNext].X), Math.Min(pf[b].X, pf[bNext].X));
			float top = Math.Max(Math.Min(pf[a].Y, pf[aNext].Y), Math.Min(pf[b].Y, pf[bNext].Y));
			float right = Math.Min(Math.Max(pf[a].X, pf[aNext].X), Math.Max(pf[b].X, pf[bNext].X));
			float bottom = Math.Min(Math.Max(pf[a].Y, pf[aNext].Y), Math.Max(pf[b].Y, pf[bNext].Y));

			if(x>=left && x<=right && y>=top && y<=bottom)
				return true;
			else
				return false;
		}
		/// <summary>
		/// Returns true if the side beginning with index "a" intersects the side beginning with index "b"
		/// (unless "a" and "b" are adjacent lets, in which case false is returned).  This is used to check if
		/// a polygon crosses over on itself.  Driven by IntersectSelf( [empty] )
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		public bool IntersectsSelf(int a, int b)
		{
			return IntersectsSelf(this, a, b);
		}
		/// <summary>
		/// Checks to see if this path crosses over itself.
		/// </summary>
		/// <returns>Returns true if the PolygonF or ContourF intersects itself, false otherwise.</returns>
		public bool IntersectsSelf()
		{
			if(this.GetType()==typeof(PolygonF))
			{
				for(int i=0; i<this.Count; i++)
					for(int j=0; j<this.Count; j++)
						if( i!=j && this.IntersectsSelf(i,j) )
							return true;
			}
			else if(this.GetType()==typeof(ContourF))
			{
				for(int i=0; i<this.Count-1; i++)
					for(int j=0; j<this.Count-1; j++)
						if( i!=j
							&& !(	(i==0&&j==this.Count-2) && this[i]==this[j+1]
								||	(j==0&&i==this.Count-2) && this[j]==this[i+1] ) // leg is not the wrap if wrapped
							&& this.IntersectsSelf(i,j) )
							return true;
			}
			return false;
		}
		/// <summary>
		/// Checks for intersection at points where contours are not joined.  Contours are joined
		/// at VertexF elements which contain this and pf as owners.
		/// </summary>
		/// <param name="pf">The other PathF which may intersect this ContourF</param>
		/// <returns>True if this contour intersects another PathF anywhere except a location where
		/// the two objects share a common VertexF, false otherwise.</returns>
		public bool IntersectsWithoutJoining(PathF pf)
		{
			if(this.Count<2 || pf.Count<2)
				return false;
			int i,j;
			bool[] owned = new bool[pf.Count];
            if( this.GetType()==typeof(PolygonF) )
			    for(i=0; i<pf.Count; i++)
				    owned[i] = pf[i].Polygons.Contains(this);
            else
                for(i=0;i<pf.Count;i++)
                    owned[i] = pf[i].Contours.Contains(this);
			bool thisIn, jNext;
            bool nextIn =   pf.GetType()==typeof(PolygonF) && this[0].Polygons.Contains(pf)
                        ||  pf.GetType()==typeof(ContourF) && this[0].Contours.Contains(pf);
			for( i=0; i<this.Count-1 || (this.GetType()==typeof(PolygonF) && i<this.Count); i++ )
			{
				thisIn = nextIn;
				if(i==this.Count-1)
                    nextIn =    pf.GetType()==typeof(PolygonF) && this[0].Polygons.Contains(pf)
                            ||  pf.GetType()==typeof(ContourF) && this[0].Contours.Contains(pf);
				else
                    nextIn =    pf.GetType()==typeof(PolygonF) && this[i+1].Polygons.Contains(pf)
                            ||  pf.GetType()==typeof(ContourF) && this[i+1].Contours.Contains(pf);
				if( !(thisIn && nextIn) )
				{
					jNext = owned[0];
					for(j=0; j<pf.Count-1 || (pf.GetType()==typeof(PolygonF) && j<pf.Count); j++)
					{
						thisIn = jNext;
						if(j==pf.Count-1)
							jNext = owned[0];
						else
							jNext = owned[j+1];
						if( !(thisIn && jNext) && PathF.Intersects(this, i, pf, j) )
							return true;
					}
				}
			}
			return false;
		}
		/// <summary>
		/// Primary constructur.  This should commonly be constructed under an owner.
		/// However, it is possible to provide a null argument for uncommon purposes.
		/// </summary>
		/// <param name="owner">The parsed object under which this polygon belongs.</param>
		public PathF(ParsedObject Owner)
		{
			this.Owner = Owner;
			if(Owner!=null)
				this.Parser = Owner.Category.Owner.Owner;
			else
				this.Parser = null;
			pbPathChangeCt = 0u;
		}
		public PathF() {}
		public virtual void ToMatlabStruct(string varName, EngMATLib.EngMATAccess mat)
		{
			int i;
			double[,] pts = new double[this.Count,2];
			for(i=0; i<this.Count; i++)
			{
				pts[i,0] = this[i].X;
				pts[i,1] = this[i].Y;
			}
			mat.SetMatrix("temp", pts);
			mat.EvaluateAsString(varName + "Vertices=temp;");
		}
		public static double _dx, _dy;
		public static int _a, _b;
		public double OrientationAt(float index)
		{
			if( this.Count <= 1 )
				return double.NaN;
			_a = (int)index;
			if( Math.Abs((float)_a-index)<0.01f )
			{
				//	The index is on a vertex
				if( _a==0 || _a==this.Count)
				{
					//	Its the first vertex in the list

					//	Wrap differently for polygons and contours
					if(this.GetType()==typeof(PolygonF))
					{
						_b = 1;
						_a = this.Count-1;
					}
					else if(this.GetType()==typeof(ContourF))
					{
						_b = 1;
						_a = 0;
					}
				}
				else if( _a==this.Count-1 )
				{
					//	Its the last index in the list

					//	Wrap differently for polygons and contours
					if(this.GetType()==typeof(PolygonF))
					{ 
						_b = 0;
						_a--;
					}
					else if(this.GetType()==typeof(ContourF))
					{
						_b = _a--;
					}
				}
				else
				{
					//	No wrapping needed, measure orientation based on surrounding vertices.
					_b = 1 + _a--;
					if(_b==this.Count)
						_b = 0;
				}
			}
			else
			{
				//	The index is between two vertices
				_b = _a+1;
				//	Wrap
				if( _b >= this.Count )
					_b = 0;
			}
			//	Calculate angle
			_dx = this[_b].X-this[_a].X;
			_dy = this[_b].Y-this[_a].Y;
			//	Return angle.
			return Math.Atan2(_dy,_dx);
		}
        /// <summary>
        /// Computes the closest distance of all path legs to a point.
        /// </summary>
        /// <param name="pt"></param>
        /// <returns></returns>
        public float DistanceToLeg(PointF pt)
        {
            float output = Single.MaxValue;
            if( this.GetType()==typeof(PolygonF) )
                output = DistanceToLeg(pt,this[this.Count-1],this[0]);
            for(int i=0;i<this.Count-1;i++)
                output = Math.Min(output,DistanceToLeg(pt,this[i],this[i+1]));
            return output;
        }
        protected static double _dist,_theta,_perpTheta,_cx,_cy,_c0,_hxa,_hxb;
        public static float DistanceToLeg(PointF pt,VertexF a,VertexF b)
        {
            //	First, find out if the point pt lies on a perpendicular intersector of the segment a-b.
            //	If not, then we only need to compute the shortest distance to one of the points a or b, which is easy.
            _dx = (double)(b.X-a.X);
            _dy = (double)(b.Y-a.Y);
            _dist = Math.Sqrt(_dx*_dx+_dy*_dy);
            _theta = Math.Acos(_dx/_dist);
            if(_dy<0.0)
                _theta = Util.TwoPi-_theta;
            _perpTheta = _theta + Util.HalfPi;
            //	There are two perpendicular bounding legs, each intersects one of the points (a or b).
            //	See if h(x) reverses sign for each leg.
            _cx = Math.Sin(_perpTheta);
            _cy = -Math.Cos(_perpTheta);
            _c0 = a.X*_cx+a.Y*_cy;
            _hxa = pt.X*_cx+pt.Y*_cy-_c0;
            //	Try leg b
            _c0 = b.X*_cx+b.Y*_cy;
            _hxb = pt.X*_cx+pt.Y*_cy-_c0;
            //	If the sign reversed, then use hx.  Otherwise, use distance to closest a or b.
            if((_hxa<0.0) == (_hxb<=0.0))	//	sign not reversed
            {
                _dx=a.X-pt.X;
                _dy=a.Y-pt.Y;
                _dist = Math.Sqrt(_dx*_dx+_dy*_dy);
                _dx=b.X-pt.X;
                _dy=b.Y-pt.Y;
                _c0 = Math.Sqrt(_dx*_dx+_dy*_dy);
                return (float)Math.Min(_c0,_dist);
            }
            else	//	sign reversed, return the absolute value of h(x)
            {
                _cx = Math.Sin(_theta);
                _cy = -Math.Cos(_theta);
                _c0 = a.X*_cx+a.Y*_cy;
                return (float)Math.Abs(pt.X*_cx+pt.Y*_cy-_c0);
            }
        }
        public System.Xml.Schema.XmlSchema GetSchema()
        {
            throw new Exception("The method or operation is not implemented.");
        }
        public void ReadXml(System.Xml.XmlReader reader)
        {
            throw new Exception("The method or operation is overriden.");
        }
        public void ReadXmlVertices(System.Xml.XmlReader reader)
        {
            int depth = reader.Depth;
            VertexF vf;
            bool reading=false;
            while( (reading || reader.Read()) && reader.Depth>depth)
            {
                reading = false;
                if((reader.NodeType == System.Xml.XmlNodeType.Element || reader.NodeType==System.Xml.XmlNodeType.Text) && reader.Name!="")
                {
                    reading = true;
                    switch(reader.Name)
                    {
                        case "v":
                            {
                                vf = new VertexF();
                                vf.SerializationPointer = reader.ReadElementContentAsInt();
                                this.Add(vf);
                            } break;
                        default:
                            throw new Exception("Unexpected field name while deserializing a PathF object.  Field name = " +reader.Name);
                    }
                }
            }
        }
        public void WriteXml(System.Xml.XmlWriter writer)
        {
            int i;

            writer.WriteStartElement("VerticesById");
            for(i=0;i<this.Count;i++)
                writer.WriteElementString("v",this[i].SerializationPointer.ToString());
            writer.WriteEndElement();
            writer.WriteElementString("Name",Name);
            writer.WriteElementString("Color",Color.ToArgb().ToString());
        }
    }
    /// <summary>
    /// [Serializable] : CollectionBase
    /// A class for polygon objects.
    /// </summary>
    [Serializable]
	public class PolygonF : PathF, ISerializable, IDeserializationCallback, IXmlSerializable
	{
		/// <summary>
		/// This enumerates all the full edges that are swallowed by the PolygonF.  Ownership is not determined.
		/// </summary>
		public EdgeArray EnumerateSwallowedEdges
		{
			get
			{
				if( this.Swallowed==null || this.Swallowed.Count==0 )
					return null;
				
				int i,j;
				EdgeArray output=null;
				EdgeArray newList=new EdgeArray();
				for(i=0; i<this.Swallowed.Count; i++)
				{
					this.Swallowed[i].EnumerateEdges(newList);
					if( newList != null )
					{
						if( output==null )
							output = newList;
						else
							for(j=0; j<newList.Count; j++)
								output.Add(newList[j]);
					}
				}

				//	Escape with a null result.
				if( output==null )
					return output;

				//	Now, remove the EdgeArrays that share vertices with another polygon.
				for(i=0; i<output.Count; i++)
					if( output[i].Begin.Polygons.Count>1 )
						output.RemoveAt(i);

				if( output.Count==0 )	//	null result
					return null;
				else if( output.Count==1 )	//	if there is only 1 item, return the output.
					return output;
				else	//	Now, if there are multiple EdgeArrays, see if they join with each other.
				{
					for(i=0; i<output.Count-1; i++)
					{
						for(j=i+1; j<output.Count; j++)
						{
							if( output[i].CanJoinWith(output[j]) )
							{
								if( output[i].End == output[j].Begin )
								{
									output[i].End=output[j].End;
								}
								else	//	output[i].Begin == output[j].End
								{
									output[i].Begin = output[j].Begin;
								}
								output.RemoveAt(j--);
							}
						}
					}
				}
				return output;
			}
		}
		#region CollectionBase overrides
		public override VertexF this[ int index ]
		{
			get
			{
				return base[index];
			}
			set
			{
				base[index] = value;
				if(!_dontStandardize && (this.Count==3 || this.Count==4))
					this.StandardizeOrder();
			}
		}
		public override int Add( VertexF vf )
		{
			int result = base.Add( vf );
            if(!_dontStandardize && (this.Count==3 || this.Count==4))
				this.StandardizeOrder();
			return result;
		}
		public override void Insert( int index, VertexF vf )
		{
			base.Insert(index, vf);
            if(!_dontStandardize && (this.Count==3 || this.Count==4))
				this.StandardizeOrder();
		}
		public override void Remove( VertexF vf )  
		{
			base.Remove(vf);
            if(!_dontStandardize && (this.Count==3 || this.Count==4))
				this.StandardizeOrder();
		}
		public new void RemoveAt(int index)
		{
			pbPathChangeCt = 0u;
			List.RemoveAt( index );
            if(!_dontStandardize && (this.Count==3 || this.Count==4))
				this.StandardizeOrder();
		}
		#endregion
		#region Event Handlers
		/// <summary>
		/// Assume the internal state of this object already reflects the add.
		/// </summary>
		/// <param name="ind"></param>
		/// <param name="vf"></param>
		/// <param name="bmp"></param>
		/// <param name="paintColor"></param>
		/// <param name="pix"></param>
		public void AfterAddVertexF(int ind, VertexF vf, Bitmap bmp, Color paintColor)
		{
			if(this.Count >=2)		//	Handle any painting and tagging.
			{				
				//	Determine if the new vertex is inside or outside of the initially existing polygon.
				//	If inside, then pix and bmp must be erased.  Otherwise, they must be painted.
				bool inside = false;
				if(this.Count>3)
				{
					this.RemoveAt(ind);
					inside = this.InPoly(vf.X, vf.Y)>0;
					this.Insert(ind, vf);
				}
				int last = VertexF.LastInd(this.Count, ind);
				int next = VertexF.NextInd(this.Count, ind);
				PolygonF pf = new PolygonF(null);
				pf.Add(this[last]);
				pf.Add(vf);
				pf.Add(this[next]);

				if(inside)	//	erase pf
				{
					//	For each pixel in the bounding rectangle of pf that is inside pf and not inside of a swallowed polygon,
					//	erase that pixel from the bitmap.
					Rectangle rect = pf.GetBoundingRect(1.0f);
					if(bmp==null)
					{
						for(int x = rect.Left; x<=rect.Right; x++)
							for(int y=rect.Top; y<=rect.Bottom; y++)
								if( pf.InPoly(x,y)>0 && !Swallowed.ContainsPoint(x,y)  && !(this.InPoly(x,y)>0) )
									Pix.RemovePixel(x,y);
					}
					else
					{
						int left = Math.Max(0, rect.Left);
						int top = Math.Max(0, rect.Top);
						int right = Math.Min(bmp.Width+1, rect.Right);
						int bottom = Math.Min(bmp.Height+1, rect.Bottom);
						for(int x = left; x<=right; x++)
							for(int y=top; y<=bottom; y++)
								if( pf.InPoly(x,y)>0 && !Swallowed.ContainsPoint(x,y)  && !(this.InPoly(x,y)>0) )
								{
									bmp.SetPixel(x,y,Color.Transparent);
									Pix.RemovePixel(x,y);
								}
					}
				}
				else	//	paint pf
				{
					//	For each pixel in the bounding rectangle of pf that is inside pf and not inside of an overlapper,
					//	erase that pixel from the bitmap.
					Rectangle rect = pf.GetBoundingRect(1.0f);
					if(bmp==null)
					{
						for(int x = rect.Left; x<=rect.Right; x++)
							for(int y=rect.Top; y<=rect.Bottom; y++)
								if( pf.InPoly(x,y)>0 && !Swallowed.ContainsPoint(x,y) && this.InPoly(x,y)>0 )
									Pix.AddPixel(x,y);
					}
					else
					{
						int left = Math.Max(0, rect.Left);
						int top = Math.Max(0, rect.Top);
						int right = Math.Min(bmp.Width+1, rect.Right);
						int bottom = Math.Min(bmp.Height+1, rect.Bottom);
						for(int x = left; x<=right; x++)
							for(int y=top; y<=bottom; y++)
								if( pf.InPoly(x,y)>0 && !Swallowed.ContainsPoint(x,y) && this.InPoly(x,y)>0 )
								{
									bmp.SetPixel(x,y,paintColor);
									Pix.AddPixel(x,y);
								}
					}
				}
			}
		}
		/// <summary>
		/// Assume the internal state of this object already reflects the move (i.e. the VertexF coordinates have
		/// already been changed).  The original coordinates are stored in dummyMovedFrom, which is "dummy" because
		/// it only contains the X and Y coordinate info.  All object links are assumed null.
		/// </summary>
		/// <param name="ind"></param>
		/// <param name="dummyMovedFrom"></param>
		/// <param name="bmp"></param>
		/// <param name="paintColor"></param>
		/// <param name="pix"></param>
		public void AfterMoveVertexF(int ind, PointF movedFrom, Bitmap bmp, Color paintColor)
		{
			VertexF vf = this[ind];
			PointF dum = new PointF(vf.X,vf.Y);
            //int i;
            //PolyFarray polys;
            //ContourFarray cons;
            //int[] indPoly, indCon;

			//dum.MimicValuesOf(vf);
			vf.X = movedFrom.X;
			vf.Y = movedFrom.Y;
            this.RemoveAt(ind);
			this.AfterRemoveVertexF(ind, vf, bmp, paintColor);

			vf.X = dum.X;
			vf.Y = dum.Y;
			this.Insert(ind, vf);
			this.AfterAddVertexF(ind, vf, bmp, paintColor);
		}
		/// <summary>
		/// When a VertexF is removed from a polygon, a Bitmap and a PixelTagger need to be adjusted accordingly.
		/// Assume the internal state of this object already reflects the removal.
		/// </summary>
		/// <param name="ind">int  The index where the VertexF object has been removed from.</param>
		/// <param name="vf">VertexF  The vertex being removed.</param>
		/// <param name="bmp">Bitmap  The bitmap to be updated.</param>
		/// <param name="paintColor">Color  The color to use when painting the bitmap (if necessary).</param>
		/// <param name="pix">PixelTagger  The PixelTagger that requires updating.</param>
		/// <returns></returns>
		public void AfterRemoveVertexF(int ind, VertexF vf, Bitmap bmp, Color paintColor)
		{
			if(this.Count >= 2)	//	Handle any painting and tagging.
			{
				//	First, determine if the removed vertex is going to be inside or outside of the resulting polygon (after removal).
				//	If inside, then pix and bmp must be painted.  Otherwise, they must be erased.
				bool inside = false;
				if(this.Count>2)
					inside = this.InPoly(vf.X, vf.Y)>0;
				else
				{
					this.EdgeGroups=null;
					this.OwnedEdges.Clear();
				}
				
				int last = VertexF.LastInd(this.Count, ind);
				int next = VertexF.NextInd(this.Count, last);
				PolygonF pf = new PolygonF(null);
				pf.Add(this[last]);
				pf.Add(vf);
				pf.Add(this[next]);
				if(inside)	//	paint
				{
					//	For each pixel in the bounding rectangle of pf that is inside pf and not inside of an overlapper,
					//	erase that pixel from the bitmap.
					Rectangle rect = pf.GetBoundingRect(1.0f);
					if(bmp==null)
					{
						for(int x = rect.Left; x<=rect.Right; x++)
							for(int y=rect.Top; y<=rect.Bottom; y++)
								if( pf.InPoly(x,y)>0 && !Swallowed.ContainsPoint(x,y) && this.InPoly(x,y)>0)
									Pix.AddPixel(x,y);
					}
					else
					{
						int left = Math.Max(0, rect.Left);
						int top = Math.Max(0, rect.Top);
						int right = Math.Min(bmp.Width+1, rect.Right);
						int bottom = Math.Min(bmp.Height+1, rect.Bottom);
						for(int x = left; x<=right; x++)
							for(int y=top; y<=bottom; y++)
								if( pf.InPoly(x,y)>0 && !Swallowed.ContainsPoint(x,y) && this.InPoly(x,y)>0)
								{
									bmp.SetPixel(x,y,paintColor);
									Pix.AddPixel(x,y);
								}
					}
				}
				else		//	erase
				{
					//	For each pixel in the bounding rectangle of pf that is inside pf and not inside of an overlapper,
					//	erase that pixel from the bitmap.
					Rectangle rect = pf.GetBoundingRect(1.0f);
					if(bmp==null)
					{
						for(int x = rect.Left; x<=rect.Right; x++)
							for(int y=rect.Top; y<=rect.Bottom; y++)
								if( pf.InPoly(x,y)>0 && !Swallowed.ContainsPoint(x,y) && !(this.InPoly(x,y)>0) )
									Pix.RemovePixel(x,y);
					}
					else
					{
						int left = Math.Max(0, rect.Left);
						int top = Math.Max(0, rect.Top);
						int right = Math.Min(bmp.Width+1, rect.Right);
						int bottom = Math.Min(bmp.Height+1, rect.Bottom);
						for(int x = left; x<=right; x++)
							for(int y=top; y<=bottom; y++)
								if( pf.InPoly(x,y)>0 && !Swallowed.ContainsPoint(x,y) && !(this.InPoly(x,y)>0) )
								{
									bmp.SetPixel(x,y,Color.Transparent);
									Pix.RemovePixel(x,y);
								}
					}
				}
			}
		}
		/// <summary>
		/// Removes this polygon from SwallowedBy.Swallowed if SwallowedBy != null
		/// </summary>
		public void RemoveSwallowedBy()
		{
			if(SwallowedBy!=null)
				SwallowedBy.Swallowed.Remove(this);
		}
		/// <summary>
		/// Restores this polygon in SwallowedBy.Swallowed if SwallowedBy != null
		/// </summary>
		public void RestoreSwallowedBy()
		{
			if(SwallowedBy!=null)
				SwallowedBy.Swallowed.Add(this);
		}
		/// <summary>
		/// Resets the pixel tagger.  This is generally triggered from a menu of ObjectParser.
		/// </summary>
		public void ResetPixelTagger()
		{
            if(Pix==null)
                Pix = new PixelTagger(this);
            else
                Pix.ResizeAndClear(this.GetBoundingRect(1.0f));
            Pix.AddPoly(this);
		}
		#endregion
		#region Swallow
		[NonSerialized]
		public PolygonF SwallowedBy;
		public PolygonFarray Swallowed;
		#endregion
		#region Serialization
		public PolygonF() {}
		public PolygonF(SerializationInfo info, StreamingContext context)
        {
#if DebugDeserialization
            ObjectParserData.DbgMsg = "Begin PolygonF";
#endif
            //	hold off standardization until inside IDeserializationCallback
			dontStandardize = true;
			try
			{
				Swallowed = (PolygonFarray)info.GetValue("Swallowed", typeof(PolygonFarray));
			}
			catch
			{
				try
				{
					Swallowed = new PolygonFarray(
						(PolygonFlist)info.GetValue("Swallowed", typeof(PolygonFlist)));
				}
				catch{ Swallowed = new PolygonFarray(); }
			}
			
			try{	Pix = (PixelTagger)info.GetValue("Pix", typeof(PixelTagger));	}
			catch{}

			try{	DbCache_UniqueId = info.GetUInt32("DbCache_UniqueId");	}
			catch{	DbCache_UniqueId = 0u;	}

			try{	DbCache_MaskFileName = info.GetString("DbCache_MaskFileName");	}
			catch{	DbCache_MaskFileName = "";	}

			OnDeserialization_Vertices = (ArrayList)info.GetValue("CollectionBase+list", typeof(ArrayList));
			
			try
			{
				OnDeserialization_StandardizeOrder = info.GetBoolean("OnDeserialization_StandardizeOrder");
			}
			catch
			{
				OnDeserialization_StandardizeOrder = true;
			}
			try{	OwnedEdges = (EdgeArray)info.GetValue("OwnedEdges", typeof(EdgeArray));	}
			catch	//	this is before polygons were kept in standardized order.
			{
				OwnedEdges = new EdgeArray();
			}
			try{	Contours = (ContourFarray)info.GetValue("Contours", typeof(ContourFarray));	}
			catch
			{
				Contours = new ContourFarray(false);
			}
			try{	ActiveContourInd = info.GetInt32("ActiveContourInd");	}
			catch
			{
				ActiveContourInd = -1;
            }
            try { Name = info.GetString("Name"); }
            catch { Name = ""; }
            try { Color = (Color)info.GetValue("Color", typeof(Color)); }
            catch { Color = Color.Transparent; }

#if DebugDeserialization
            ObjectParserData.DbgMsg = "End PolygonF";
#endif
        }
		public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
		{	
			this.StandardizeOrder();
			info.AddValue("OnDeserialization_StandardizeOrder", false);
			info.AddValue("Swallowed", Swallowed);
            if( this.Owner.Category.Owner.LastSerializationType==SerializationType.Binary )
			    info.AddValue("Pix", Pix);
			info.AddValue("CollectionBase+list", base.InnerList);
			info.AddValue("DbCache_UniqueId", DbCache_UniqueId);
			info.AddValue("DbCache_MaskFileName", DbCache_MaskFileName);
			info.AddValue("OwnedEdges", OwnedEdges);
			info.AddValue("Contours", Contours);
			info.AddValue("ActiveContourInd", ActiveContourInd);
            info.AddValue("Name", Name);
            info.AddValue("Color",Color);
		}
		[NonSerialized]
		public ArrayList OnDeserialization_Vertices;
		public bool OnDeserialization_StandardizeOrder;
		public void OnDeserialization(object sender)
		{
			//ObjectParserData.CurrentlyActive.SetStatusBar("PolygonF:  "+this.Owner.Category.Name + "/" + this.Owner.Name + "/" + this.Owner.Polygons.IndexOf(this).ToString());
			int i;
			//	Suppress standardization function while the polygon is being read in.
			this.dontStandardize=true;
			if(OnDeserialization_Vertices != null)
			{
				VertexF vf;
				for(i=0; i<OnDeserialization_Vertices.Count; i++)
				{
					vf = (VertexF)OnDeserialization_Vertices[i];
					if(vf.Polygons == null)
						vf.Polygons = new PolyFarray(true);
					vf.Polygons.Add(this);
					this.Add(vf);
				}
			}
			//	Unsuppress standardization
			this.dontStandardize=false;

			//ObjectParserData.CurrentlyActive.SetStatusBar("aPolygonF:  "+this.Owner.Category.Name + "/" + this.Owner.Name + "/" + this.Owner.Polygons.IndexOf(this).ToString());

			OnDeserialization_Vertices = null;
			if(Pix == null)
				try
				{
					Pix = new PixelTagger(this);
				}
				catch
				{
					Swallowed.Clear();
					Pix = new PixelTagger(this);
				}
			else
				Pix.Owner = this;

			//ObjectParserData.CurrentlyActive.SetStatusBar("bPolygonF:  "+this.Owner.Category.Name + "/" + this.Owner.Name + "/" + this.Owner.Polygons.IndexOf(this).ToString());

			for(i=0; i<Swallowed.Count; i++)
				if(Swallowed[i]==null || Swallowed[i].Count==0)
					Swallowed.RemoveAt(i--);
				else
					Swallowed[i].SwallowedBy = this;

			//ObjectParserData.CurrentlyActive.SetStatusBar("cPolygonF:  "+this.Owner.Category.Name + "/" + this.Owner.Name + "/" + this.Owner.Polygons.IndexOf(this).ToString());

			for(i=0; i<OwnedEdges.Count; i++)
				if(OwnedEdges[i]==null)
					OwnedEdges.RemoveAt(i--);
				else
					OwnedEdges[i].Owner = this;

			//ObjectParserData.CurrentlyActive.SetStatusBar("dPolygonF:  "+this.Owner.Category.Name + "/" + this.Owner.Name + "/" + this.Owner.Polygons.IndexOf(this).ToString());

			if(OnDeserialization_StandardizeOrder)
				this.StandardizeOrder();

			//ObjectParserData.CurrentlyActive.SetStatusBar("ePolygonF:  "+this.Owner.Category.Name + "/" + this.Owner.Name + "/" + this.Owner.Polygons.IndexOf(this).ToString());

			for(i=0; i<Contours.Count; i++)
			{
				Contours[i].PolyOwner = this;
				Contours[i].Owner = this.Owner;
			}

			//ObjectParserData.CurrentlyActive.SetStatusBar("fPolygonF:  "+this.Owner.Category.Name + "/" + this.Owner.Name + "/" + this.Owner.Polygons.IndexOf(this).ToString());
		}
		/// <summary>
		/// This reduces the size of the boolean bitmap to its minimum required size, thus providing
		/// some compression before the data is saved.
		/// </summary>
		public void ReducePix()
		{
			Pix.ResizeAndMaintain(this.GetBoundingRect(0.0f));
		}
        public string Address
        {
            get
            {
                if(this.Owner!= null)
                    return this.Owner.Address + '/' + this.Owner.Polygons.IndexOf(this).ToString();
                return "-1";
            }
        }
        [NonSerialized]
        public int SerializationPointer=-1;
        public System.Xml.Schema.XmlSchema GetSchema()
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public new void ReadXml(System.Xml.XmlReader reader)
        {
            this.StandardizesAutomatically = false;
            PolygonF poly;
            ContourF con;
            Edge ed;
            int depth = reader.Depth;
            bool reading=false;
            while( (reading || reader.Read()) && reader.Depth>depth)
            {
                reading = false;
                if((reader.NodeType == System.Xml.XmlNodeType.Element || reader.NodeType==System.Xml.XmlNodeType.Text) && reader.Name!="")
                {
                    reading = true;
                    switch(reader.Name)
                    {
                        case "Id":
                            {
                                this.SerializationPointer = reader.ReadElementContentAsInt();
                            } break;
                        case "ActiveContourInd":
                            {
                                this.ActiveContourInd = reader.ReadElementContentAsInt();
                            } break;
                        case "Contours":
                            {
                                this.Contours = new ContourFarray(false);
                                //  Step in 1 level and parse each "contour", add it to the list.
                                reading = false;
                            } break;
                        case "Contour":
                            {
                                con = new ContourF(this);
                                this.Contours.Add(con);
                                con.ReadXml(reader);
                            } break;
                        case "DbCache_MaskFileName":
                            {
                                this.DbCache_MaskFileName = reader.ReadElementContentAsString();
                            } break;
                        case "DbCache_UniqueId":
                            {
                                this.DbCache_UniqueId = (uint)reader.ReadElementContentAsLong();
                            } break;
                        case "OwnedEdges":
                            {
                                this.OwnedEdges = new EdgeArray();
                                //  Step in 1 level and parse each "contour", add it to the list.
                                reading = false;
                            } break;
                        case "Edge":
                            {
                                ed = new Edge(this);
                                this.OwnedEdges.Add(ed);
                                ed.ReadXml(reader);//  Dummy vertices will be used for each edge element, this will later be fixed at a higher level.
                            } break;
                        case "SwallowedPolygonsById":
                            {
                                this.Swallowed = new PolygonFarray();
                                //  Step in 1 level and parse each "polygon", add it to the list.
                                reading = false;
                            } break;
                        case "p":
                            {
                                poly = new PolygonF();
                                this.Swallowed.Add(poly);
                                poly.SerializationPointer = reader.ReadElementContentAsInt(); // These dummy polygons will later be fixed at a higher level.
                            } break;
                        case "VerticesById":
                            {
                                ReadXmlVertices(reader);
                            } break;
                        case "Name":
                            {
                                Name = reader.ReadElementContentAsString() ;
                            } break;
                        case "Color":
                            {
                                this.Color = Color.FromArgb(reader.ReadElementContentAsInt());
                                if(this.Color.A==0)
                                    this.Color = Color.Transparent;
                            } break;
                        default: throw new Exception("Unexpected field encounterd while reading XML for a PolygonF object.  Field name = " + reader.Name);
                    }
                }
            }
            this.StandardizesAutomatically = true;
            if(this.Contours!=null)
            {
                this.Contours.AutoSort = true;
                this.Contours.Sort();
            }
        }

        public new void WriteXml(System.Xml.XmlWriter writer)
        {
            int i;

            writer.WriteStartElement("Polygon");

            writer.WriteElementString("Id",this.SerializationPointer.ToString());

            writer.WriteElementString("ActiveContourInd",this.ActiveContourInd.ToString());

            writer.WriteStartElement("Contours");
            for(i=0;i<this.Contours.Count;i++)
                Contours[i].WriteXml(writer);
            writer.WriteEndElement();

            writer.WriteElementString("DbCache_MaskFileName",this.DbCache_MaskFileName);

            writer.WriteElementString("DbCache_UniqueId",this.DbCache_UniqueId.ToString());

            writer.WriteStartElement("OwnedEdges");
            for(i=0;i<this.OwnedEdges.Count;i++)
                OwnedEdges[i].WriteXml(writer);
            writer.WriteEndElement();

            writer.WriteStartElement("SwallowedPolygonsById");
            for(i=0;i<this.Swallowed.Count;i++)
                writer.WriteElementString("p", Swallowed[i].SerializationPointer.ToString());
            writer.WriteEndElement();

            base.WriteXml(writer);

            writer.WriteEndElement();
        }
		#endregion
		#region Heavy Math and Static Resources
		[NonSerialized]
		public static PolygonF NullPoly;
		static PolygonF()
		{
			_angles = new ArrayList(4);
			for(int i=0; i<4; i++)
				_angles.Add(new DoubleSort(0.0,0));
			if(_signCrossProduct == null)
			{
				//	Initialize private memory for InPolygon() function.
				_signCrossProduct = new float[500];
				_dotProduct = new float[500];
				_quad = new float[500];
				_diffQuad = new float[500];
				//	Initialize memory for RandomEdgePoints function.
				_distMap = new float[500];
			}
			NullPoly = new PolygonF(null);
		}
		[NonSerialized]
		public static float[] _signCrossProduct, _dotProduct, _quad, _diffQuad;
		/// <summary>
		/// Memory for use by RandomEdgePoints.
		/// </summary>
		[NonSerialized]
		public static float[] _distMap;
		/// <summary>
		/// Determines if the point (xx,yy) is inside the polygon.
		/// </summary>
		/// <param name="xx"></param>
		/// <param name="yy"></param>
		/// <returns>-1 if outside of polygon, 1 if inside of polygon, and 0 if on polygon edge.</returns>
		public int InPoly(float xx, float yy)
		{
			xx-=0.5f;		//	Not sure why this has to be done, but it does.
			yy-=0.5f;
			if (this.Count < 3)
				return -1;

			float x1, x2, y1, y2;	//	The point-centered vertex coordinates for the current and next vertex.
			float crossProduct=0.0f;
			if(_signCrossProduct.Length<this.Count)
			{
				_signCrossProduct = new float[2*this.Count];
				_dotProduct = new float[2*this.Count];
				_quad = new float[2*this.Count];
				_diffQuad = new float[2*this.Count];
			}
			x2 = this[this.Count-1].X-xx;
			y2 = this[this.Count-1].Y-yy;
			int i,ii;
			for(i=0; i<this.Count; i++)
			{
				//	Compute the cross-product and dot-product.
				x1=x2;
				y1=y2;
				x2=this[i].X-xx;
				y2=this[i].Y-yy;
				crossProduct = x1*y2-x2*y1;
				if(crossProduct<0.0) _signCrossProduct[i]=-1.0f;
				else if(crossProduct>0.0) _signCrossProduct[i]=1.0f;
				else _signCrossProduct[i]=0.0f;
				_dotProduct[i] = x1*x2+y1*y2;

				//	Denote quadrant affiliation for each vertex
				if(x1>0.0 && y1>0.0) _quad[i] = 0.0f;
				else if(x1<=0.0 && y1>0.0) _quad[i] = 1.0f;
				else if(x1<=0.0 && y1<=0.0) _quad[i] = 2.0f;
				else if(x1>0.0 && y1<=0.0) _quad[i] = 3.0f;
			}
			//	Compute the DiffQuad (the quadrant changes for each change in vertex)
			for(i=0; i<this.Count; i++)
			{
				ii=i+1;
				if(ii==this.Count) ii=0;
				
				//	Compute the DiffQuad
				_diffQuad[i] = _quad[ii]-_quad[i];
				//	Replace 3 by -1 and -3 by 1;
				if(_diffQuad[i]==3.0f) _diffQuad[i]=-1.0f;
				if(_diffQuad[i]==-3.0f) _diffQuad[i]=1.0f;

				// Any DiffQuad with a value of 2 should have the same sign as the cross product;
				if(Math.Abs(_diffQuad[i])==2.0f) _diffQuad[i] = 2.0f*_signCrossProduct[i];
			}

			float sumDiffQuad = 0.0f;
			for(i=0; i<this.Count; i++) sumDiffQuad += _diffQuad[i];

			bool b = false;
			for(i=0; i<this.Count; i++)
				b = b | (_signCrossProduct[i]==0.0f && _dotProduct[i]<=0.0f);
			if(b)
				return 0;	//	The point lies on the polygon
			if(sumDiffQuad != 0.0f)
				return 1;								//	The point lies inside of the polygon.
			return -1;									//	The point lies outside of the polygon.
		}
		public int InPolyNotSwallowed(float xx, float yy)
		{
			int output = InPoly(xx,yy);
			if(output > -1 && Swallowed != null)
			{
                int result;
                for(int i=0;i<Swallowed.Count;i++)
                {
                    result = Swallowed[i].InPoly(xx,yy);
                    if(result==1)
                        return -1;
                    else if(result==0)
                        output = 0;
                }
			}
			return output;
		}
		public int InPoly(int x, int y)
		{
			return InPoly( (float)x+0.5f, (float)y+0.5f );
		}
		public int InPolyNotSwallowed(int x,  int y)
		{
			return InPolyNotSwallowed( (float)x+0.5f, (float)y+0.5f );
		}
		#endregion
        #region Database (Deprecated)
        public uint DbCache_UniqueId=0u;
        public string DbCache_MaskFileName="";
        /// <summary>
        /// For database functionality with the BLOB datatype.
        /// </summary>
        public byte[] VerticesByteArray
        {
            get
            {
                byte[] output = new byte[this.Count*2*4];
                byte[] smallOut;
                int ii;
                for(int i=0;i<this.Count;i++)
                {
                    ii = 4*2*i;
                    smallOut = System.BitConverter.GetBytes(this[i].X);
                    output[ii]=smallOut[0];
                    output[ii+1]=smallOut[1];
                    output[ii+2]=smallOut[2];
                    output[ii+3]=smallOut[3];

                    smallOut = System.BitConverter.GetBytes(this[i].Y);
                    output[ii+4]=smallOut[0];
                    output[ii+5]=smallOut[1];
                    output[ii+6]=smallOut[2];
                    output[ii+7]=smallOut[3];
                }
                return output;
            }
        }
        /// <summary>
        /// For database functionality with the BLOB datatype.
        /// </summary>
        public byte[] SwallowedByteArray
        {
            get
            {
                if(this.Swallowed.Count==0)
                    return new byte[0];
                byte[] output = new byte[4*this.Swallowed.Count];
                byte[] smallOut;
                int ii;
                for(int i=0;i<Swallowed.Count;i++)
                {
                    ii = 4*i;
                    smallOut = System.BitConverter.GetBytes(this.Swallowed[i].DbCache_UniqueId);
                    output[ii] = smallOut[0];
                    output[ii+1] = smallOut[1];
                    output[ii+2] = smallOut[2];
                    output[ii+3] = smallOut[3];
                }
                return output;
            }
        }
        #endregion
        #region Edges
        /// <summary>
        /// This enumerates all the full edges that are owned by the PolygonF (not swallowed).
        /// This does not insure against the rare case when an edge pair may not actually be
        /// three edges due to the twin-owner polygon splitting off at index i
        /// then rejoining at index i+1.  This rare situation can be detected when the
        /// other polygon does not have an edge that pairs with one of these Edges.
        /// Such a situation should be handled outside the scope of this function (i.e.
        /// when EdgeGroup objects are being created).
        /// </summary>
        public void EnumerateEdges(EdgeArray output)
        {
            ArrayList junctions = EnumerateJunctions();
            if(junctions.Count<=1)
            {
                output.Add(new Edge(this));	//	Full enclosure
                return;
            }
            int j;
            int ii,jj;
            /*
            if(this.GetType()!=typeof(PolygonF))	//	Initial edge of ContourF
            {
                ii = 0;
                jj = (int)junctions[0];
                if(jj>ii)
                    output.Add(new Edge(this,this[ii],this[jj]));
            }
            */
            for(int i=0;i<junctions.Count;i++)
            {
                j=i+1;
                if(this.GetType()==typeof(PolygonF) || j<junctions.Count)	//	Only wrap for PolygonF
                {
                    if(j==junctions.Count)
                        j=0;
                    ii = (int)junctions[i];
                    jj = (int)junctions[j];
                    output.Add(new Edge(this,this[ii],this[jj]));
                }
            }
            /*
            if(this.GetType()!=typeof(PolygonF))	//	Final edge of ContourF
            {
                ii = (int)junctions[junctions.Count-1]; ;
                jj = this.Count-1;
                if(jj>ii)
                    output.Add(new Edge(this,this[ii],this[jj]));
            }
            */
        }
        /// <summary>
        /// Fills the EdgeCache with a list of edges for this PathF, then calculates EdgeGroups.
        /// </summary>
        public void CalcEdges()
        {
            int i,j;
            //	First, enumerate the full edges in this polygon.
            if(EdgeCache==null)
                EdgeCache = new EdgeArray();
            else
                EdgeCache.Clear();
            EnumerateEdges(EdgeCache);

            //	Next, determine a list of polygons whose EdgeArrays must also be enumerated.
            int ind=0;
            ArrayList pfcounts = new ArrayList();
            PolyFarray pflist = new PolyFarray(false);
            PolygonF poly;
            for(i=0;i<EdgeCache.Count;i++)
            {
                //	Get other Polygon objects which might own the edge.
                pflist.Add(EdgeCache[i].TwinOwner());
            }
            //	For each possible owner, compile a unique list (pfUnique)
            PolyFarray pfUnique = new PolyFarray(true,pflist.Count);
            for(j=0;j<pflist.Count;j++)
            {
                poly = pflist[j];
                //	Compile a list of unique polygons and conduct unique enumerations of Edge objects.
                if(poly!=null && !poly.IsNull && pfUnique.BinarySearch(poly)<0)
                {
                    pfUnique.InsertSorted(poly);
                    if(poly.EdgeCache==null)
                        poly.EdgeCache = new EdgeArray();
                    else
                        poly.EdgeCache.Clear();
                    poly.EnumerateEdges(poly.EdgeCache);
                }
            }
            //	Now generate the edge group from the cache
            //	Now determine the EdgeGroups
            EdgeGroup eg;
            if(EdgeGroups==null)
                EdgeGroups=new EdgeGroupArray();
            else
                EdgeGroups.Clear();
            int low=0,num=0;
            for(i=0;i<EdgeCache.Count;i++)
            {
                //	Get an edge group for each member of the EdgeCache
                eg = new EdgeGroup();
                //	If a currently owned edge has the same description as EdgeCache[i], then
                //	pull it out and mark it as the dominant edge.  Otherwise, go with EdgeCache[i].
                ind = this.OwnedEdges.FindSameEdgeAs(EdgeCache[i]);
                if(ind>=0)
                {
                    eg.Add(this.OwnedEdges[ind]);
                    eg.DominantInd=0;
                }
                else
                {
                    eg.Add(EdgeCache[i]);
                    eg.DominantInd=-1;
                }

                low+= num;
                poly = pflist[i];
                if(poly!=null && !poly.IsNull)
                {
                    ind = poly.OwnedEdges.FindTwinOf(eg[0]);
                    if(ind>=0)
                    {
                        eg.Add(poly.OwnedEdges[ind]);
                        eg.DominantInd = eg.Count-1;
                    }
                    else
                    {
                        ind = poly.EdgeCache.FindTwinOf(eg[0]);
                        if(ind>=0)
                            eg.Add(poly.EdgeCache[ind]);
                    }
                }
                EdgeGroups.Add(eg);
            }
        }
        /// <summary>
        /// Validates the member EdgeGroups and any related OwnedEdges (including some OwnedEdges of
        /// other paths that have a common EdgeGroup with this path). Returns invalid edges.
        /// New valid EdgeGroups are created if necessary (these new edges are not claimed by any PolygonF yet).
        /// </summary>
        public EdgeGroupArray ValidateEdges()
        {
            if(this.Count<3)
                return new EdgeGroupArray();
            //	This will be the old edge pairs which need to be eliminated because
            //	they differ with the new assessment.
            EdgeGroupArray fepa = EdgeGroups;
            if(fepa==null)
                fepa = new EdgeGroupArray();
            //	Reinitialize memory for the recalculation (so fepa is not disturbed).
            EdgeGroups = new EdgeGroupArray();
            //	Re-calculates the edge situation.
            CalcEdges();
            //	Eliminates the elements of fepa which are the same as EdgeGroups.
            fepa.EliminateSimilaritiesFrom(EdgeGroups);
            //	The remaining elements of fepa must have their members removed from 
            //	any PolygonF.OwnedEdges list.
            int i,j;
            Edge fe;
            for(i=0;i<fepa.Count;i++)
            {
                //	Remove any owned edges, but leave fepa[j].DominantInd alone (for Undo purposes).
                fe = fepa[i].DominantEdge;
                if(fe != null)
                {
                    for(j=0;j<fe.Owner.OwnedEdges.Count;j++)
                    {
                        if(fe.Owner.OwnedEdges[j] == fe)
                        {
                            fe.Owner.OwnedEdges.RemoveAt(j);
                            break;
                        }
                    }
                }
            }
            //  See if any OwnedEdges lack representation in the EdgeGroups variable
            bool hasRepresentation;
            for(i=0;i<OwnedEdges.Count;i++)
            {
                hasRepresentation = false;
                for(j=0;j<EdgeGroups.Count;j++)
                {
                    if(EdgeGroups[j].DominantEdge==OwnedEdges[i])
                    {
                        hasRepresentation=true;
                        break;
                    }
                }
                if(!hasRepresentation)
                {
                    if(OwnedEdges[i].Group==null)
                    {
                        OwnedEdges[i].Group = new EdgeGroup();
                        OwnedEdges[i].Group.Add(OwnedEdges[i]);
                        OwnedEdges[i].Group.DominantInd = 0;
                    }
                    fepa.Add(OwnedEdges[i].Group);
                    OwnedEdges.RemoveAt(i--);
                }
            }
            //	return the result
            return fepa;
        }
        /// <summary>
        /// The full edges owned by this polygon.
        /// </summary>
        public EdgeArray OwnedEdges;
        /// <summary>
        /// This is a holding spot for edges.  Information contained here will vary.
        /// </summary>
        [NonSerialized]
        public EdgeArray EdgeCache;
        /// <summary>
        /// Holds the entire list of edges for an entire object as well as 
        /// the associated edges for neighboring objects.  This is meant to
        /// be updated when this polygon becomes active.  The owned edges of
        /// other polygons, of this polygon, and remaining unowned edges
        /// are contained herein.  You should only trust the contents of this
        /// variable after CalcEdges() has been called, otherwise it may not
        /// contain updated information.
        /// </summary>
        [NonSerialized]
        public EdgeGroupArray EdgeGroups;
        /// <summary>
        /// This is executed before saving.
        /// </summary>
        public void TidyOwnedEdges()
        {
            int j;
            EdgeArray newOwnedEdges = new EdgeArray();
            EdgeGroup group;
            Edge ed;
            for(j=0;j<this.EdgeGroups.Count;j++)
            {
                group = EdgeGroups[j];
                if(group!=null)
                {
                    ed = group.DominantEdge;
                    if(ed!=null && ed.Owner==this && ed.ClaimedByOwner && ed.Owner.Contains(ed.Begin) && ed.Owner.Contains(ed.End))
                        newOwnedEdges.Add(ed);
                }
            }
            OwnedEdges = newOwnedEdges;
        }
        /// <summary>
        /// Enumerates the indices for all bridges of the PathF.
        /// </summary>
        /// <param name="output">An ArrayList of integers that are indexes into the PathF.</param>
        /// <param name="uniqueSortedIndices">true if you want the output to be sorted with
        /// only unique entries, false otherwise.</param>
        public void EnumerateBridgeIndices(ArrayList output,bool uniqueSortedIndices)
        {
            if(Owner!=null)
            {
                int i;
                OcclusionBridge bridge;
                for(i=0;i<Owner.Bridges.Count;i++)
                {
                    bridge=Owner.Bridges[i];
                    if(bridge.Pf1==this)
                        output.Add(this.IndexOf(bridge.Vf1));
                    if(bridge.Pf2==this)
                        output.Add(this.IndexOf(bridge.Vf2));
                }
                if(uniqueSortedIndices)
                {
                    output.Sort();
                    for(i=1;i<output.Count;i++)
                        if(output[i]==output[i-1])
                            output.RemoveAt(i--);
                }
            }
        }
        /// <summary>
        /// Enumerates all the integer indices of this path where there are edge boundaries.
        /// </summary>
        /// <param name="output"></param>
        public ArrayList EnumerateJunctions()
        {
            int i,j,ii,iiPlus,jj,jjMinus,iiTwin,jjTwin,ct,direction;
            PathF pf;

            //	Output a list of integers
            ArrayList output = new ArrayList();
            if(this.Count < 3)
                return output;

            //	Get the index of any occlusion bridges.  Those are automatically junctions.
            EnumerateBridgeIndices(output,false);

            //	Owners of two adjacent vertices.
            PolyFarray thisOwn = this[0].Polygons;
            PolyFarray lastOwn;
            for(i=1;i<this.Count;i++)
            {
                //	Update list of owners for this pair of vertices.
                lastOwn = thisOwn;
                thisOwn = this[i].Polygons;
                if(!lastOwn.ContentsAreEqualTo(thisOwn))	//	Check for a change in owners
                {
                    //	If there was an increase in the number of owners, then 
                    //	the junction is forward.
                    if(thisOwn.Count > lastOwn.Count)
                    {
                        output.Add(i);
                    }
                    //	If there was a decrease, the junction is backwards.
                    else if(thisOwn.Count < lastOwn.Count)
                        output.Add(i-1);
                    //	If there is no change, then there are two junctions.
                    else
                    {
                        output.Add(i-1);
                        output.Add(i);
                    }
                }
            }
            //	Handle the wrap from the last vertex to vertex 0.
            lastOwn = thisOwn;
            thisOwn = this[0].Polygons;
            if(!lastOwn.ContentsAreEqualTo(thisOwn))
            {
                //	If there was an increase in the number of owners, then 
                //	the junction is forwards.
                if(thisOwn.Count > lastOwn.Count)
                {
                    output.Insert(0,0);
                }
                //	If there was a decrease, the junction is backwards.
                else if(thisOwn.Count < lastOwn.Count)
                    output.Add(this.Count-1);
                //	If there is no change, then there are two junctions.
                else
                {
                    output.Insert(0,0);
                    output.Add(this.Count-1);
                }
            }
            //	sort
            output.Sort();
            //	remove references to -1
            while(output.Count>=1 && (int)output[0]==-1)
                output.RemoveAt(0);
            //	remove duplicates
            for(i=1;i<output.Count;i++)
            {
                if((int)output[i]==(int)output[i-1])
                    output.RemoveAt(i--);
            }

            //	There may be an edge which begins with this PolygonF and another pf, pulls out
            //	at a random index ii from pf, comes back at ii+1 and continues.
            //	This needs to be handled by tracing the edge of "this" and "pf" simultaneously.
            for(i=0;i<output.Count;i++)
            {
                //	First, get beginning (ii) and ending (jj) indexes on this polygon.
                j=i+1;
                if(j==output.Count)
                    j=0;
                ii=(int)output[i];
                jj=(int)output[j];
                iiPlus = ii+1;
                if(iiPlus==this.Count)
                    iiPlus=0;
                jjMinus = jj-1;
                if(jjMinus==-1)
                    jjMinus=this.Count-1;

                //	Determine who shares the edge by finding 'pf'
                pf=null;
                thisOwn = this[iiPlus].Polygons;
                lastOwn = this[jjMinus].Polygons;
                //	If there are only two or three edge vertices (a single segment), no splitting will occur.
                //	Check the 1-inner vertices because the segment is large enough (>3 vertices)
                if(iiPlus!=jj && iiPlus!=jjMinus && thisOwn.Count>1 && lastOwn.Count>1)
                {
                    for(ct=0;ct<thisOwn.Count;ct++)
                    {
                        //	An owner of this vertex
                        pf=thisOwn[ct];

                        //	Which is not the current polygon and not contained within the list of owners of the last vertex
                        if(pf!=this && lastOwn.Contains(pf))
                        {
                            //	We will search between iiPlus+1 to jjMinus-1 for a break
                            ii = iiPlus;
                            jj = jjMinus;
                            //	iiTwin and jjTwin are indices into polygon pf (matching ii and jj).
                            iiTwin = pf.IndexOf(this[iiPlus]);
                            jjTwin = pf.IndexOf(this[jjMinus]);
                            //	If its a polygon, it must go in the opposite direction
                            direction=-1;
                            //	Otherwise, find out which direction the vertices travel.
                            if(pf.GetType()!=typeof(PolygonF))
                                direction = this.Direction(pf,ii,jj,iiTwin,jjTwin);

                            //	Here we trace the contour of 'this' and 'pf' simultaneously.
                            //	If the rare event occurs, a splitter is inserted (turning 1 edge into 3).
                            do
                            {
                                //	pf counter increment
                                iiTwin+=direction;

                                //	Wrap iiTwin if necessary
                                if(iiTwin==-1)
                                {
                                    if(pf.GetType()!=typeof(PolygonF))	//	no wrapping
                                        break;
                                    iiTwin = pf.Count-1;
                                }
                                else if(iiTwin==pf.Count)
                                {
                                    if(pf.GetType()!=typeof(PolygonF))	//	no wrapping
                                        break;
                                    iiTwin=0;
                                }
                                //	this counter increment
                                //	Wrap ii if necessary
                                if(++ii==this.Count)
                                {
                                    if(this.GetType()!=typeof(PolygonF))	//	no wrapping
                                        break;
                                    ii=0;
                                }

                                //	The rare event has occurred!!  One of the edges has pulled off.
                                //	Insert a splitter and update iiTwin
                                if(this[ii]!=pf[iiTwin])
                                {
                                    //	Insert the index before ii
                                    if(ii==0)
                                        output.Insert(++i,this.Count-1);
                                    else
                                        output.Insert(++i,ii-1);
                                    //	Insert the index ii
                                    output.Insert(++i,ii);
                                    //	Update iiTwin
                                    iiTwin = pf.IndexOf(this[ii]);
                                }
                            } while(ii!=jjMinus);
                        }
                    }
                }
            }
            //	output is guaranteed to be sorted.
            return output;
        }
        [NonSerialized]
        protected bool dontStandardize,_dontStandardize;
        /// <summary>
        /// True if this list standardizes its order automatically, false otherwise.
        /// </summary>
        public bool StandardizesAutomatically
        {
            get { return !dontStandardize; }
            set { dontStandardize = !value; }
        }
        /// <summary>
        /// This makes sure that the polygon points are ordered such that moving from 0 to 1 implies that the inside of the polygon is to the 
        /// right-perpendicular direction of vec(0-->1) while the outside of the polygon is to the left-perpendicular direction of vec(0-->1).
        /// If !this.OrderIsStandardized, then this.Reverse() is executed.
        /// </summary>
        public void StandardizeOrder()
        {
            if(!dontStandardize)	//	dontStandardize == false
            {
                dontStandardize = true;
                if(this.Count>2 && !OrderIsStandardized)
                    this.Reverse();
                dontStandardize = false;
            }
        }
        /// <summary>
        /// True if the order is already standardized, false otherwise.
        /// </summary>
        public bool OrderIsStandardized
        {
            get
            {
                if(this.Count>2)
                {
                    double[] angleSums = AngleSums;
                    return angleSums[1] < angleSums[0];
                }
                return true;
            }
        }
        /// <summary>
        /// The sum of all angles in the polygon.  Element 0 gives the leftward angles and element 1 gives the rightward angles.
        /// </summary>
        public double[] AngleSums
        {
            get
            {
                if(this.Count <=2)
                    return null;
                double[] output = new double[2];
                output[0]=output[1]=(double)this.Count*Math.PI;
                int head;
                double tailAngle,headAngle,forward;
                //double lastDist, dist;
                headAngle = VertexF.Angle(this[this.Count-1],this[0]);
                //dist=Math.Sqrt(Math.Pow(this[this.Count-1].X-this[0].X,2.0)+Math.Pow(this[this.Count-1].Y-this[0].Y,2.0));
                for(int i=0;i<this.Count;i++)
                {
                    head=i+1;
                    if(head>=this.Count)
                        head=0;
                    //	The tail angle is just the previous head angle.
                    tailAngle = headAngle;

                    //	The had angle must be greater than the tail angle.
                    headAngle = VertexF.Angle(this[i],this[head]);
                    //headAngle = Math.Floor(tailAngle/Util.TwoPi)*Util.TwoPi+headAngle;
                    //if(headAngle<tailAngle)
                    //	headAngle+=Util.TwoPi;

                    //lastDist = dist;
                    //dist=Math.Sqrt(Math.Pow(this[i].X-this[head].X,2.0)+Math.Pow(this[i].Y-this[head].Y,2.0));

                    forward=Util.AngleDistance(headAngle,tailAngle);

                    //output[0]+=(Util.TwoPi-forward)*lastDist;
                    //output[1]+=forward*dist;

                    output[0]+=forward;
                    output[1]-=forward;
                }
                return output;
            }
        }
        /// <summary>
        /// Reverses the contents of the list.
        /// </summary>
        public void Reverse()
        {
            //	Hold off standardization
            _dontStandardize = dontStandardize;
            dontStandardize = true;

            int rev,i,half = (this.Count-1)/2;
            VertexF vf;
            for(i=0;i<=half;i++)
            {
                //	compute the reverse index.
                rev = this.Count-i-1;
                //	swap
                vf = this[rev];
                this[rev]=this[i];
                this[i]=vf;
            }
            //	Reverse any owned edges.
            for(i=0;i<this.OwnedEdges.Count;i++)
            {
                vf = OwnedEdges[i].Begin;
                OwnedEdges[i].Begin = OwnedEdges[i].End;
                OwnedEdges[i].End = vf;
            }
            //	Return standardization
            dontStandardize = _dontStandardize;
        }
        #endregion
        #region Ribboning
        public override double EdgeLength(VertexF vfBegin,VertexF vfEnd,bool ascending)
        {
            if(vfBegin==vfEnd)
                return this.Circumfrence;

            double output = 0.0;
            int I = IndexOf(vfBegin);
            int J = IndexOf(vfEnd);
            int i=I,j;
            double dx,dy;
            VertexF vfi,vfj;

            if(I==-1 || J==-1)
                return 0.0f;

            while(true)
            {
                //	j denotes the next vertex
                if(ascending)
                {
                    j=i+1;
                    if(j==Count)
                        j=0;
                }
                else
                {
                    j=i-1;
                    if(j==-1)
                        j=Count-1;
                }

                vfi = this[i];
                vfj = this[j];
                dx = (double)(vfj.X-vfi.X);
                dy = (double)(vfj.Y-vfi.Y);
                output += Math.Sqrt(dx*dx+dy*dy);

                if(J==j)	//	This is the last trip through
                    return output;

                //	Increment (or decrement)
                if(ascending)
                {
                    i++;
                    if(i==Count)
                        i=0;
                }
                else
                {
                    i--;
                    if(i==-1)
                        i=Count-1;
                }
            }
        }
        /// <summary>
        /// Returns a list of edge points that all fall on the edge of the polygon, from beginning to end,
        /// where the distance between the edge points is denoted as dpix.
        /// </summary>
        /// <param name="dpix"></param>
        /// <returns></returns>
        public override PointF[] EdgePoints(float dpix)
        {
            float carry = 0.5f;
            return EdgePoints(dpix,this[0],this[0],true,ref carry);
        }
        /// <summary>
        /// Returns a list of edge points that fall within this Edge, from beginning to end,
        /// where the distance between the edge points is denoted as dpix.
        /// </summary>
        /// <param name="dpix">The distance between edge points in pixels.</param>
        /// <returns></returns>
        public override PointF[] EdgePoints(float dpix,VertexF vfBegin,VertexF vfEnd,bool ascending,ref float carryOver)
        {
            //  Cannot continue unless there are at least 2 vertices in this path.
            if(Count < 2)
                return null;

            //	Standardize the order so that moving forward around the border implies that the inside of the polygon is right-perpendicular.
            this.StandardizeOrder();

            //	i denotes the current vertex
            int i=IndexOf(vfBegin);
            int lastInd = IndexOf(vfEnd);

            if(i==-1 || lastInd==-1)
                throw new ArgumentException("vfBegin and vfEnd must be a member of this PolygonF","vfBegin or vfEnd");

            if(carryOver<0.0f || carryOver >= 1.0f)
                throw new ArgumentException("Value must be non-negative and less than 1","carryOver");

            int len = (int)(EdgeLength(vfBegin,vfEnd,ascending)/dpix+(double)carryOver);
            if(len<1)
            {
                carryOver+=(float)EdgeLength(vfBegin,vfEnd,ascending)/dpix;
                return new PointF[0];
            }

            //	Allocate the output
            PointF[] output = new PointF[len];

            //	Denotes the amount of weight to be carried over.
            float wPix,dx,dy,wSteps;
            int ct=0,j;

            //	i denotes the current vertx
            do
            {
                //	j denotes the next vertex
                if(ascending)
                    j=i+1;
                else
                    j=i-1;
                if(j==this.Count)
                    j=0;
                if(j<0)
                    j=this.Count-1;
                //	denote the slope for this interval.
                dx = this[j].X-this[i].X;
                dy = this[j].Y-this[i].Y;
                //cumdist += (float)Math.Sqrt(dx*dx+dy*dy);
                //	count the number of pixels for this interval.
                wPix = (float)Math.Sqrt((double)(dx*dx+dy*dy));
                wSteps = wPix/dpix;
                if(wSteps + carryOver < 1.0f)	//	if there isn't enough to extract an edge point, then move to the next vertex.
                {
                    carryOver+=wSteps;
                }
                else	//	otherwise, within this vertex, get all the edge points.
                {
                    //	Compute the unit vector.
                    dx/=wPix;
                    dy/=wPix;
                    //  Now, carryOver has been passed, its complement awaits us.
                    carryOver = 1.0f-carryOver;
                    if(ct<output.Length) // This safety is here due to roundoff error when EdgeLength / dpix is near an integer.
                    {
                        do
                            output[ct++] = new PointF(this[i].X+dx*carryOver*dpix,this[i].Y+dy*carryOver*dpix);
                        while(++carryOver <= wSteps && ct<output.Length); // Also includes safety
                    }
                    carryOver=carryOver%1.0f;	//	carry over the remainder to the next iteration
                }
                i=j;
            } while(i!=lastInd);

            return output;
        }
        #endregion
        #region Patch Pairs Analysis
        /// <summary>
        /// Finds pairs of circles within the polygon boundary.
        /// </summary>
        /// <param name="output">A list of Circle[2] pairs is appended.</param>
        /// <param name="nTry">The number of pairs to try and obtain.</param>
        /// <param name="radius">The diameter of both circles.</param>
        /// <param name="separation">The separation between the edges of the circles.</param>
        public void FindPairsWithin(ArrayList output,int nTry,int diameter,int separation)
        {
            int i;
            Random rand = ObjectParserData.Rand;

            //  The circles
            Circle acir,bcir=null;
            float sep = (float)separation +  (float)diameter;
            double or;
            Point a,b;
            for(i=0;i<nTry;i++)
            {
                a = this.Pix.RandomPointInPix;
                or = (rand.NextDouble()-0.5)*Util.TwoPi;
                b = new Point((int)(0.5f+(float)a.X+sep*(float)Math.Cos(or)),(int)(0.5f+(float)a.Y+sep*(float)Math.Sin(or)));
                if(Pix.PixelIsTagged(b.X,b.Y))
                {
                    acir = new Circle(new PointF(a.X,a.Y),(float)diameter/2.0f,false);
                    bcir = new Circle(new PointF(b.X,b.Y),(float)diameter/2.0f,false);
                    if(Pix.FullyContained(acir) && Pix.FullyContained(bcir))
                        output.Add(new Circle[] { acir,bcir });
                }
            }
        }
        /// <summary>
        /// Finds pairs of circles across the polygon boundary.
        /// </summary>
        /// <param name="output">A list of Circle[2] pairs is appended as {inner, outer}.</param>
        /// <param name="nTry">The number of pairs to try and obtain.</param>
        /// <param name="radius">The radius of both circles.</param>
        /// <param name="separation">The separation between the edges of the circles.</param>
        public void FindPairsAcross(ArrayList output,int nTry,int diameter,int separation)
        {
            int i;
            Util udum = new Util(); // holds static
            Random rand = ObjectParserData.Rand;

            //  Sample the edge points on the clockwise polygon.
            PointF[] ep = EdgePoints(this.Circumfrence/(float)nTry);

            //  Orientation at each edge point.
            //double[] or = new double[ep.Length];
            //or[0] = Math.Atan2(ep[1].Y-ep[ep.Length-1].Y, ep[1].X-ep[ep.Length-1].X);
            //or[ep.Length-1] = Math.Atan2(ep[0].Y-ep[ep.Length-2].Y,ep[0].X-ep[ep.Length-2].X);
            //for(i=1; i<=ep.Length-2; i++)
            //    or[i] = Math.Atan2(ep[i+1].Y-ep[i-1].Y,ep[i+1].X-ep[i-1].X);

            //  The inner and outer circles
            Circle inner,outer=null;
            double sep = (double)separation;
            float hsep = (float)sep/2.0f + (float)diameter/2.0f;
            double off,or;
            PointF vec,mid;
            Point a,b;
            for(i=0;i<ep.Length;i++)
            {
                or = (rand.NextDouble()-0.5)*Util.TwoPi;
                off = (rand.NextDouble()-0.5)*sep;
                vec = new PointF((float)Math.Cos(or-Util.HalfPi),(float)Math.Sin(or-Util.HalfPi));
                mid = new PointF(
                    ep[i].X+(float)off*vec.X,
                    ep[i].Y+(float)off*vec.Y);
                a = new Point(
                    (int)(0.5f + mid.X - hsep*vec.X),
                    (int)(0.5f + mid.Y - hsep*vec.Y));
                b = new Point(
                    (int)(0.5f + mid.X + hsep*vec.X),
                    (int)(0.5f + mid.Y + hsep*vec.Y));
                inner = null;
                if(this.Pix.PixelIsTagged(a.X,a.Y))
                {
                    if(!this.Pix.PixelIsTagged(b.X,b.Y))
                    {
                        inner = new Circle(new PointF(a.X,a.Y),(float)diameter/2.0f,false);
                        outer = new Circle(new PointF(b.X,b.Y),(float)diameter/2.0f,false);
                    }
                }
                else if(this.Pix.PixelIsTagged(b.X,b.Y))
                {
                    inner = new Circle(new PointF(b.X,b.Y),(float)diameter/2.0f,false);
                    outer = new Circle(new PointF(a.X,a.Y),(float)diameter/2.0f,false);
                }
                if(inner!=null && this.Pix.FullyContained(inner) && this.Pix.FullyExcluded(outer))
                    output.Add(new Circle[] { inner,outer });
            }
        }
        #endregion
		/// <summary>
		/// True if this polygon has a vertex inside the polygon pf or if the polygon pf has a vertex inside of this.  Both cases are tested.
		/// </summary>
		/// <param name="pf">PolygonF</param>
		/// <returns></returns>
		public bool IntersectsWith(PolygonF pf)
		{
			return Intersects(this,pf);
		}
		/// <summary>
		/// True if pf1 and pf2 overlap in any way, including a containment relationship.
		/// </summary>
		/// <param name="pf1"></param>
		/// <param name="pf2"></param>
		/// <returns></returns>
		public static bool Overlaps(PolygonF pf1, PolygonF pf2)
		{
			if(pf1==pf2)
				return false;
			if(pf1==null || pf2==null)
				return false;
			//	First, check for an intersection.
			if(Intersects(pf1,pf2))
				return true;
			// We require the vertex to not be owned by the other poly.
			VertexF vf1 = pf1.GetAnyVertexNotPartOf(pf2);
			VertexF vf2 = pf2.GetAnyVertexNotPartOf(pf1);
			if(vf1==null && vf2==null)
				return true;
			else if(vf1==null || vf2==null)	//	One polygon is a complete subset of another polygon.
			{
				PolygonF pfLesser = pf1;
				PolygonF pfGreater = pf2;
				if(vf2==null)
				{
					pfLesser = pf2;			//	pfLesser is the smaller one.
					pfGreater = pf1;
				}
				try
				{
					PointF pt = pfLesser.RandomPointInPoly;
					if(pfGreater.InPoly(pt.X, pt.Y)>0)
						return true;
					else
						return false;
				}
				catch		//	pf is a straight line.  the new "polygon" has no volume, so no overlap.
				{
					return false;
				}
			}
			int contained1 = pf1.InPoly(vf2.X, vf2.Y);
			int contained2 = pf2.InPoly(vf1.X, vf1.Y);
			if(contained1>0 || contained2>0)
				return true;
			
			return false;
		}
		/// <summary>
		/// True if this polygon contains pf entirely.  False if there are any intersections or only partial overlap.
		/// </summary>
		/// <param name="pf"></param>
		/// <returns></returns>
		public bool ContainsPoly(PolygonF pf)
		{
			//	must have at least 3 vertices.
			if(this.Count < 3)
				return false;
			//	First, check for an intersection.
			if(Intersects(this,pf))
				return false;
			//	Second, check for containment.
			if(this.InPoly(pf[0].X, pf[0].Y) > 0)
				return true;
			return false;
		}
		/// <summary>
		/// True if pf contains this polygon entirely.  False if there are any intersections or only partial overlap.
		/// </summary>
		/// <param name="pf"></param>
		/// <returns></returns>
		public bool IsContainedByPoly(PolygonF pf)
		{
			//	pf must have at least 3 vertices.
			if(pf.Count<3)
				return false;
			//	First, check for an intersection.
			if(Intersects(this,pf))
				return false;
			//	Second, check for containment.  If any of the members of pf is inside, then all are inside
			//	because an intersection has been ruled out.
			if(this.InPoly(pf[0].X, pf[0].Y) <= 0)
				return true;
			return false;
		}
		public override float Circumfrence
		{
			get
			{
				if(this.Count < 2)
					return 0.0f;
				float output = VertexF.Distance(this[this.Count-1], this[0]);
                int len = this.Count-1;
				for(int i=0; i<len; i++)
					output += VertexF.Distance(this[i], this[i+1]);
				return output;
			}
		}
		/// <summary>
		/// Primary constructur.  This should commonly be constructed under an owner.
		/// However, it is possible to provide a null argument for uncommon purposes.
		/// </summary>
		/// <param name="owner">The parsed object under which this polygon belongs.</param>
		public PolygonF(ParsedObject Owner) : base(Owner)
		{
			Swallowed = new PolygonFarray();
			SwallowedBy = null;
			Pix = new PixelTagger(this);
			Contours = new ContourFarray(false);
            OwnedEdges = new EdgeArray();
			//	Enable automatic standardization of vertices.
			dontStandardize = false;
			ActiveContourInd=-1;
		}
		/// <summary>
		/// Caches the pixels in a boolean bitmap to prevent from evaluating the InPoly function too many times.
		/// </summary>
		public PixelTagger Pix;
		/*
		public void InventoryActuallySwallowed(out PolygonF[] actuallySwallowed, out PolygonF[] notActuallySwallowed)
		{
			int ct = 0;
			bool[] swallowed = new bool[Swallowed.Count];
			for(int i=0; i<Swallowed.Count; i++)
			{
				swallowed[i] = Swallowed[i].IsContainedByPoly(this);
				if(swallowed[i]) ct++;
			}
			actuallySwallowed = new PolygonF[ct];
			notActuallySwallowed = new PolygonF[ct];
			int ctSwallowed = 0;
			int ctNot = 0;
			for(int i=0; i<Swallowed.Count; i++)
				if(swallowed[i])
					actuallySwallowed[ctSwallowed++] = Swallowed[i];
				else
					notActuallySwallowed[ctNot++] = Swallowed[i];
		}
		*/
		public override string ToString()
		{
			return Owner.Category.Path+"/"+Owner.Category.Name+"/"+ Owner.Name + "/Poly_" + Convert.ToString(this.Owner.Polygons.IndexOf(this));
		}
		/// <summary>
		/// A random point in the polygon.
		/// </summary>
		public PointF RandomPointInPoly
		{
			get
			{
				if(this.Count <= 2)
					return new PointF(0.0f, 0.0f);
				Rectangle rect = this.GetBoundingRect(0.0f);
				Random rand = ObjectParserData.Rand;

				PointF tryPt = new PointF(
					(float)rect.Left + (float)rect.Width*(float)rand.NextDouble(), 
					(float)rect.Top + (float)rect.Height*(float)rand.NextDouble() );

				int ct = 0;
				while( InPoly(tryPt.X, tryPt.Y)<=0 )
				{
					tryPt.X = (float)rect.Left + (float)rect.Width*(float)rand.NextDouble(); 
					tryPt.Y = (float)rect.Top + (float)rect.Height*(float)rand.NextDouble();
					if(++ct > 1000)
						throw new ApplicationException("An improbable stochastic limit was reached at in PointF.RandomPointInPoly.");
				}
				return tryPt;
			}
		}
		/// <summary>
		/// The contours owned by this PolygonF.
		/// </summary>
		public ContourFarray Contours;
		/// <summary>
		/// The index of the active contour.
		/// </summary>
		public int ActiveContourInd;
		/// <summary>
		/// The active contour (null if there is no active contour).
		/// </summary>
		public ContourF ActiveContour
		{
			get
			{
				if(ActiveContourInd<0 || ActiveContourInd>=Contours.Count)
					return null;
				return Contours[ActiveContourInd];
			}
			set
			{
				int ind = Contours.IndexOf(value);
				if(ind>=0)
					ActiveContourInd = ind;
			}
		}
		/// <summary>
		/// Creates a new contour, adds it to the member variable Contours, then returns the created contour.
		/// If there exists an element of Contours that is empty, a new polygon is not created and 
		/// the pre-existing empty polygon is returned.
		/// </summary>
		/// <returns></returns>
		public ContourF CreateNewContour()
		{
			for(int i=0; i<Contours.Count; i++)
				if(Contours[i].Count == 0)
					ActiveContourInd = i;
			ContourF cf = new ContourF(this);
			Contours.Add(cf);
			return cf;
		}
		/// <summary>
		/// Gets the previous contour in line to be activated.
		/// </summary>
		public ContourF PreviousContour
		{
			get
			{
				if(Contours!=null && Contours.Count>0)
				{
					int i=ActiveContourInd-1;
					if(i<0)
						i=Contours.Count-1;
					return Contours[i];
				}
				return null;
			}
		}
		/// <summary>
		/// Gets the next contour in line to be activated.
		/// </summary>
		public ContourF NextContour
		{
			get
			{
				if(Contours!=null && Contours.Count>0)
				{
					int i=ActiveContourInd+1;
					if(i>=Contours.Count)
						i=0;
					return Contours[i];
				}
				return null;
			}
		}
		/// <summary>
		/// Constructs a table of all junction arrays related to any pair of PathF objects in a set.
		/// The set of PathF objects includes each member of 'Contours'.
		/// </summary>
		public Xjunction[,][] Xjunctions
		{
			get
			{
				Xjunction[,][] output = new Xjunction[this.Contours.Count,this.Contours.Count][];
				int i,j;
				ContourF cf;
				for(i=0; i<this.Contours.Count; i++)
				{
					cf = this.Contours[i];
					for(j=i; j<this.Contours.Count; j++)
					{
						//	Measure intersection
						output[i,j] = Xjunction.Junctions(cf,this.Contours[j]);
						//	Symmetry
						if(j!=i)
							output[j,i] = output[i,j];
					}
				}
				XjunctionCache = output;
				return output;
			}
		}
		[NonSerialized]
		public Xjunction[,][] XjunctionCache;
		/// <summary>
		/// Sends this to matlab as a structure.  Clears the 'temp' varaible.
		/// </summary>
		/// <param name="varName"></param>
		/// <param name="mat"></param>
		public override void ToMatlabStruct(string varName, EngMATLib.EngMATAccess mat)
		{
			int i;
			string ss;
			if(this.Swallowed.Count==0)
				mat.EvaluateAsString(varName + "Swallowed = [];");
			else
			{
				for(i=0; i<this.Swallowed.Count; i++)
				{
					ss = varName + "Swallowed(" + (i+1).ToString() + ").";
					Swallowed[i].ToMatlabStruct(ss, mat);
				}
			}
			if(this.Contours.Count==0)
				mat.EvaluateAsString(varName + "Contours= [];");
			else
			{
				for(i=0; i<this.Contours.Count; i++)
				{
					ss = varName + "Contours(" + (i+1).ToString() + ").";
					Contours[i].ToMatlabStruct(ss, mat);
				}
			}
			base.ToMatlabStruct(varName, mat);
		}
        /// <summary>
        /// Looks at OwnedEdges from an older format of data (where contours denoted junctions) and merges them into edges compatible with the newer format (where contours are ignored).
        /// </summary>
        public void MergeEdges()
        {
            //  Skip this whole routine if there is a single OwnedEdge which is a full enclosure.
            if(OwnedEdges ==null || OwnedEdges.Count!=1 || OwnedEdges[0].IsFullEnclosure==false)
            {
                //  Update the "EdgeCache" variable.
                this.CalcEdges();
                //  For each member of EdgeCache, see if one or many members of OwnedEdges can be identified with it.
                EdgeArray updated = new EdgeArray();
                //  First, pull members of EdgeCache which are exactly equal to an OwnedEdge;
                int i,j,ind;
                for(i=0; i<EdgeCache.Count; i++)
                {
                    ind = OwnedEdges.FindSameEdgeAs(EdgeCache[i]);
                    if( ind>=0 )
                    {
                        updated.Add( OwnedEdges[ind] );
                        OwnedEdges.RemoveAt(ind);
                        EdgeCache.RemoveAt(i--);
                    }
                }
                //  Now, simple matches are in "updated" and also removed from OwnedEdges and EdgeCache.
                //  Next, for each OwnedEdges, find an EdgeCache with a matching "Begin" member,
                //  then build a string of elements from OwnedEdges where each "End" corresponds to a "Begin" from another member of OwnedEdges.
                //  Continue building the string until the "End" element from OwnedEdges matches an "End" element from the original EdgeCache member.
                //  If this process succeeds, remove the string from OwnedEdges and the EdgeCache member, place the EdgeCache member into "updated".
                
                bool ownsEnd, ownsBegin;
                Edge whole;
                EdgeArray train = new EdgeArray();
                for(i=0; i<EdgeCache.Count; i++)
                {
                    //  Initialize the train
                    whole = EdgeCache[i];
                    train.Clear();
                    for(j=0; j<OwnedEdges.Count; j++)
                    {
                        if( OwnedEdges[j].Begin==whole.Begin )
                        {
                            train.Add(OwnedEdges[j]);
                            OwnedEdges.RemoveAt(j);
                            break;
                        }
                    }
                    //  Check to see if a train can be started
                    if( train.Count > 0 )
                    {
                        j=0;
                        //  On a successful match, this exits on "break", otherwise on the conditional.
                        while( j<OwnedEdges.Count )
                        {
                            //  Check through all possibilities to see if the train can grow.
                            for(j=0; j<OwnedEdges.Count; j++)
                            {
                                //  If the train can grow
                                ownsEnd = whole.OwnsIndex(this.IndexOf(OwnedEdges[j].End));
                                ownsBegin = whole.OwnsIndex(this.IndexOf(OwnedEdges[j].Begin));
                                if( !ownsEnd || !ownsBegin )
                                {
                                    //  If only one section is owned, then this OwnedEdge is definitely not going to fit because it crosses over a real edge border.
                                    if( ownsEnd != ownsBegin )
                                        OwnedEdges.RemoveAt(j);
                                    //  Exit, try the next EdgeCache, this one has failed.
                                    j=OwnedEdges.Count;
                                    break;
                                }
                                if( OwnedEdges[j].Begin == train[train.Count-1].End )
                                {
                                    //  Grow it
                                    train.Add(OwnedEdges[j]);
                                    OwnedEdges.RemoveAt(j);   //  Dismiss it from other considerations
                                    //  Exit and check to see if the growth provided our answer.
                                    j=-1;
                                    break;
                                }
                            }
                            //  If the train had grown
                            if( j<OwnedEdges.Count )
                            {
                                //  See if the train matches the whole
                                if(whole.End==train[train.Count-1].End)
                                {
                                    //  Success, update
                                    updated.Add(whole);
                                    break;
                                }
                            }
                        }
                    }
                }
                OwnedEdges = updated;
            }
        }
		/// <summary>
		/// Adds this polygon back into the list of owners for each of the VertexF members of the inner list.
		/// This is the inverse mutator of RemoveVertexF().  Child contour objects have this method called also.
		/// </summary>
		public override void ReclaimVertices()
		{
			int i;
			for(i=0; i<this.Count; i++)
            {
				if(!this[i].Polygons.Contains(this))
					this[i].Polygons.Add(this);
            }
			for(i=0; i<this.Contours.Count; i++)
				Contours[i].ReclaimVertices();
		}
		/// <summary>
		/// Removes this PolygonF from the list of owners for each VertexF.  Each VertexF is left a member
		/// of this PolygonF in order to accomodate "undo" functionality.
		/// This method is also called for any child ContourF objects.
		/// </summary>
		public override void RemoveVertexF()
		{
			int i;
			for(i=0; i<this.Count; i++)
				this[i].RemoveOwner(this);
			for(i=0; i<this.Contours.Count; i++)
				Contours[i].RemoveVertexF();
        }
        /*
        /// <summary>
        /// Eliminates unnecessary vertices based on geometry, without respect to the contents of VertexF.Contours or VertexF.Polygons.
        /// </summary>
        /// <returns>Gets a polygon with fewer vertices than this one.  The ouptut is a new PolygonF constructed from some of the VertexF objects in this PolygonF.</returns>
        public PolygonF GeometricReduction(double etol)
        {
            if( this.Count > 3 )
            {
                int i, ii;
                double[] angle = new double[this.Count];
                double[] curv = new double[this.Count];
                double[] dist = new double[this.Count]; // distance from previous vertex to this vertex.
                double dx, dy, dist0;
                //  Determines whether an index has been eliminated
                bool[] elim = new bool[curv.Length];
                //  First vertex backward-differenced orientation
                VertexF vf1 = this[this.Count-1];
                VertexF vf2 = this[0];
                dx = vf2.X-vf1.X;
                dy = vf2.Y-vf1.Y;
                dist[0] = dist0 = Math.Sqrt(dx*dx+dy*dy);
                angle[0] = Math.Atan2(dy,dx);
                //  Remaining vertices
                for(i=1; i<this.Count; i++)
                {
                    ii = i-1;
                    vf1 = this[ii];
                    vf2 = this[i];
                    dx = vf2.X-vf1.X;
                    dy = vf2.Y-vf1.Y;
                    dist[i] = Math.Sqrt(dx*dx+dy*dy);
                    angle[i] = Math.Atan2(dy,dx);
                    curv[ii] = angle[i]-angle[ii];
                    curv[ii] = Math.Atan2(Math.Sin(curv[ii]),Math.Cos(curv[ii]));
                    elim[ii] = Math.Abs(curv[ii])<=etol;
                }
                //  Last vertex curvature & distance
                i=0;
                ii=this.Count-1;
                curv[ii] = angle[i]-angle[ii];
                curv[ii] = Math.Atan2(Math.Sin(curv[ii]),Math.Cos(curv[ii]));
                elim[ii] = Math.Abs(curv[ii])<=etol;
                //  Eliminate all consecutive points with sum(abs(curvature*distance)) less than etol.
                //  Begin by using a doublesort to find the minima sum(abs(curvature*distance))
                //int iLow,iHigh,iiLow,iiHigh;
                //DoubleSort[] csort = new DoubleSort[curv.Length];
                //for(i=0; i<csort.Length; i++)
                //{
                //   ii = i+1;
                //    if(ii==csort.Length)
                //        ii=0;
                //    csort[i] = new DoubleSort( Math.Abs(curv[i])*(dist[i]+dist[ii])/2.0, i );
                //}
                //Array.Sort(csort);
                //  Reverse sort
                //int[] reverse = new int[curv.Length];
                //for(i=0; i<csort.Length; i++)
                //    reverse[csort[i].Ind] = i;
                PolygonF output = new PolygonF();
                output.StandardizesAutomatically = false;
                for(i=0; i<elim.Length; i++)
                    if(!elim[i])
                        output.Add(this[i]);
                return output;
            }
            return null;
        }
        */
    }
	[Serializable]
	public class ContourF : PathF, ISerializable, IDeserializationCallback, IXmlSerializable
	{
		public static ContourF NullValue;
		static ContourF()
		{
			NullValue = new ContourF();
		}
		public ContourF()
		{
			this.CatData = new ContourCategorizationData();
		}
		/// <summary>
		/// Constructs a ContourF object with the PolyOwner field given by the argument.
		/// </summary>
		/// <param name="PolyOwner"></param>
		public ContourF(PolygonF PolyOwner) : base(null)
		{
			this.Owner = PolyOwner.Owner;
			this.PolyOwner = PolyOwner;
			if(PolyOwner==null)
				this.Parser=null;
			else
				this.Parser = PolyOwner.Parser;
			this.CatData = new ContourCategorizationData();
		}
		#region Serialization
		public ContourF(SerializationInfo info, StreamingContext context)
		{
#if DebugDeserialization
            ObjectParserData.DbgMsg = "Begin ContourF";
#endif
			OnDeserialization_Vertices = (ArrayList)info.GetValue("CollectionBase+list", typeof(ArrayList));
			try
			{
				CatData = (ContourCategorizationData)info.GetValue("CatData", typeof(ContourCategorizationData));
			}
			catch
			{
				this.CatData = new ContourCategorizationData();
			}
            try { Name = info.GetString("Name"); }
            catch { Name = ""; }
            try { Color = (Color)info.GetValue("Color",typeof(Color)); }
            catch { Color = Color.Transparent; }
#if DebugDeserialization
            ObjectParserData.DbgMsg = "End ContourF";
#endif
		}
		public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
		{	
			info.AddValue("CollectionBase+list", base.InnerList);
			info.AddValue("CatData", CatData);
            info.AddValue("Name",Name);
            info.AddValue("Color",Color);
		}
		[NonSerialized]
		public ArrayList OnDeserialization_Vertices;
		public void OnDeserialization(object sender)
		{
			int i;

			//	Suppress standardization function while the polygon is being read in.
			if(OnDeserialization_Vertices != null)
			{
				VertexF vf;
				for(i=0; i<OnDeserialization_Vertices.Count; i++)
				{
					vf = (VertexF)OnDeserialization_Vertices[i];
					if(vf.Contours == null)
						vf.Contours = new ContourFarray(true);
					vf.Contours.Add(this);
					this.Add(vf);
				}
			}
			OnDeserialization_Vertices = null;
			this.Owner = this.PolyOwner.Owner;
		}
        public string Address
        {
            get
            {
                if(this.PolyOwner!= null)
                    return this.PolyOwner.Address + '/' + this.PolyOwner.Contours.IndexOf(this).ToString();
                return "-1";
            }
        }
        public new System.Xml.Schema.XmlSchema GetSchema()
        {
            throw new Exception("The method or operation is not implemented.");
        }
        public new void ReadXml(System.Xml.XmlReader reader)
        {
            int depth = reader.Depth;
            bool reading=false;
            while( (reading || reader.Read()) && reader.Depth>depth)
            {
                reading = false;
                if((reader.NodeType == System.Xml.XmlNodeType.Element || reader.NodeType==System.Xml.XmlNodeType.Text) && reader.Name!="")
                {
                    reading = true;
                    switch(reader.Name)
                    {
                        case "ContourCategorizationData":
                            {
                                this.CatData = new ContourCategorizationData();
                                this.CatData.ReadXml(reader);
                            } break;
                        case "VerticesById":
                            {
                                ReadXmlVertices(reader);
                            } break;
                        case "Name":
                            {
                                this.Name = reader.ReadElementContentAsString();
                            } break;
                        case "Color":
                            {
                                this.Color = Color.FromArgb(reader.ReadElementContentAsInt());
                                if(this.Color.A==0)
                                    this.Color = Color.Transparent;
                            } break;
                        default: throw new Exception("Unexpected field encounterd while reading XML for a PolygonF object.  Field name = " + reader.Name);
                    }
                }
            }
        }
        public new void WriteXml(System.Xml.XmlWriter writer)
        {

            writer.WriteStartElement("Contour");

            CatData.WriteXml(writer);

            base.WriteXml(writer);

            writer.WriteEndElement();
        }
		#endregion
		[NonSerialized]
		public PolygonF PolyOwner=null;

		/// <summary>
		/// The categorization information for this contour.
		/// </summary>
		public ContourCategorizationData CatData;
		public override void ToMatlabStruct(string varName, EngMATLib.EngMATAccess mat)
		{
			CatData.ToMatlabStruct(varName + "Cat.", mat);
			base.ToMatlabStruct(varName, mat);
		}
		/// <summary>
		/// Adds this contour back into the list of owners for each of the VertexF members of the inner list.
		/// This is the inverse mutator of RemoveVertexF().
		/// </summary>
		public override void ReclaimVertices()
		{
			int i;
			for(i=0; i<this.Count; i++)
            {
				if(!this[i].Contours.Contains(this))
					this[i].Contours.Add(this);
            }
		}
		/// <summary>
		/// Removes this contour from the list of owners for each VertexF.  Each VertexF is left a member
		/// of this path in order to accomodate "undo" functionality.
		/// </summary>
		public override void RemoveVertexF()
		{
			for(int i=0; i<this.Count; i++)
				this[i].RemoveOwner(this);
		}
    }
}